Skip to content

Commit

Permalink
[darwin-framework-tool] Automatically check for leaks on shutdown if …
Browse files Browse the repository at this point in the history
…enable_leak_checking is true (project-chip#35936)
  • Loading branch information
vivien-apple authored Oct 8, 2024
1 parent 6034c87 commit bd8803a
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 1 deletion.
8 changes: 8 additions & 0 deletions examples/darwin-framework-tool/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ declare_args() {

# Disable generating compiler database by default
generate_compilation_database = false

# Enable automatic leak checks before the application exits
enable_leak_checking = false
}

sdk = "macosx"
Expand Down Expand Up @@ -219,6 +222,7 @@ executable("darwin-framework-tool") {
"commands/provider/OTASoftwareUpdateInteractive.mm",
"commands/storage/Commands.h",
"commands/storage/StorageManagementCommand.mm",
"debug/LeakChecker.mm",
"logging/logging.mm",
"main.mm",
]
Expand Down Expand Up @@ -280,6 +284,10 @@ executable("darwin-framework-tool") {
defines += [ "MTR_ENABLE_PROVISIONAL=1" ]
}

if (enable_leak_checking) {
defines += [ "DFT_ENABLE_LEAK_CHECKING=1" ]
}

public_configs = [ ":config" ]

output_dir = root_out_dir
Expand Down
28 changes: 28 additions & 0 deletions examples/darwin-framework-tool/debug/LeakChecker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) 2024 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

#pragma once

/*
* This function performs a memory leak check if the build flag `enable_leak_checking` is set to true
* If leaks are detected, it overrides the provided exit code with `EXIT_FAILURE`.
*
* @param exitCode The initial exit code to return if no leaks are detected or if leak checking is disabled.
* @return `EXIT_FAILURE` if leaks are detected and leak checking is enabled; otherwise, the original `exitCode`.
*/
int ConditionalLeaksCheck(int exitCode);
70 changes: 70 additions & 0 deletions examples/darwin-framework-tool/debug/LeakChecker.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (c) 2024 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

#include "LeakChecker.h"

#import <Foundation/Foundation.h>
#include <unistd.h> // For getpid()

@interface LeakChecker : NSObject
- (BOOL)hasMemoryLeaks;
@end

@implementation LeakChecker

- (BOOL)hasMemoryLeaks
{
pid_t pid = getpid();
auto * pidString = [NSString stringWithFormat:@"%d", pid];

auto * task = [[NSTask alloc] init];
task.launchPath = @"/usr/bin/leaks";
task.arguments = @[ pidString ];

auto * pipe = [NSPipe pipe];
task.standardOutput = pipe;
task.standardError = pipe;

NSFileHandle * fileHandle = [pipe fileHandleForReading];
[task launch];
[task waitUntilExit];

int exitCode = [task terminationStatus];
if (exitCode) {
NSData * data = [fileHandle readDataToEndOfFile];
NSString * output = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@", output);
return YES;
}

return NO;
}

@end

int ConditionalLeaksCheck(int exitCode)
{
#ifdef DFT_ENABLE_LEAK_CHECKING
auto * leakChecker = [[LeakChecker alloc] init];
if ([leakChecker hasMemoryLeaks]) {
return EXIT_FAILURE;
}
#endif // DFT_ENABLE_LEAK_CHECKING

return exitCode;
}
5 changes: 4 additions & 1 deletion examples/darwin-framework-tool/main.mm
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#import <Matter/Matter.h>

#import "debug/LeakChecker.h"
#import "logging/logging.h"

#include "commands/bdx/Commands.h"
Expand All @@ -35,6 +36,7 @@

int main(int argc, const char * argv[])
{
int exitCode = EXIT_SUCCESS;
@autoreleasepool {
dft::logging::Setup();

Expand All @@ -49,6 +51,7 @@ int main(int argc, const char * argv[])
registerCommandsStorage(commands);
registerCommandsConfiguration(commands);
registerClusters(commands);
return commands.Run(argc, (char **) argv);
exitCode = commands.Run(argc, (char **) argv);
}
return ConditionalLeaksCheck(exitCode);
}
16 changes: 16 additions & 0 deletions src/darwin/Framework/Matter.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,8 @@
B4E262172AA0CF2000DBA5BC /* RemoteDataModelLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = B4E262132AA0C7A300DBA5BC /* RemoteDataModelLogger.h */; };
B4E2621B2AA0D02000DBA5BC /* SleepCommand.mm in Sources */ = {isa = PBXBuildFile; fileRef = B4E262192AA0D01D00DBA5BC /* SleepCommand.mm */; };
B4E2621E2AA0D02D00DBA5BC /* WaitForCommissioneeCommand.mm in Sources */ = {isa = PBXBuildFile; fileRef = B4E2621C2AA0D02A00DBA5BC /* WaitForCommissioneeCommand.mm */; };
B4F773CA2CB54B61008C6B23 /* LeakChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = B4F773C72CB54B61008C6B23 /* LeakChecker.h */; };
B4F773CB2CB54B61008C6B23 /* LeakChecker.mm in Sources */ = {isa = PBXBuildFile; fileRef = B4F773C82CB54B61008C6B23 /* LeakChecker.mm */; };
B4FCD56A2B5EDBD300832859 /* MTRDiagnosticLogsType.h in Headers */ = {isa = PBXBuildFile; fileRef = B4FCD5692B5EDBD300832859 /* MTRDiagnosticLogsType.h */; settings = {ATTRIBUTES = (Public, ); }; };
B4FCD5702B603A6300832859 /* Commands.h in Headers */ = {isa = PBXBuildFile; fileRef = B4FCD56D2B603A6300832859 /* Commands.h */; };
B4FCD5712B603A6300832859 /* DownloadLogCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = B4FCD56E2B603A6300832859 /* DownloadLogCommand.h */; };
Expand Down Expand Up @@ -818,6 +820,8 @@
B4E262132AA0C7A300DBA5BC /* RemoteDataModelLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RemoteDataModelLogger.h; sourceTree = "<group>"; };
B4E262192AA0D01D00DBA5BC /* SleepCommand.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SleepCommand.mm; sourceTree = "<group>"; };
B4E2621C2AA0D02A00DBA5BC /* WaitForCommissioneeCommand.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WaitForCommissioneeCommand.mm; sourceTree = "<group>"; };
B4F773C72CB54B61008C6B23 /* LeakChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LeakChecker.h; sourceTree = "<group>"; };
B4F773C82CB54B61008C6B23 /* LeakChecker.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LeakChecker.mm; sourceTree = "<group>"; };
B4FCD5692B5EDBD300832859 /* MTRDiagnosticLogsType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRDiagnosticLogsType.h; sourceTree = "<group>"; };
B4FCD56D2B603A6300832859 /* Commands.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Commands.h; sourceTree = "<group>"; };
B4FCD56E2B603A6300832859 /* DownloadLogCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DownloadLogCommand.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -874,6 +878,7 @@
037C3CA82991A44B00B7EEE2 /* darwin-framework-tool */ = {
isa = PBXGroup;
children = (
B4F773C92CB54B61008C6B23 /* debug */,
039145E02993102B00257B3E /* main.mm */,
03F430A52994100000166449 /* controller */,
039547092992DB02006D42A8 /* editline */,
Expand Down Expand Up @@ -1543,6 +1548,15 @@
path = delay;
sourceTree = "<group>";
};
B4F773C92CB54B61008C6B23 /* debug */ = {
isa = PBXGroup;
children = (
B4F773C72CB54B61008C6B23 /* LeakChecker.h */,
B4F773C82CB54B61008C6B23 /* LeakChecker.mm */,
);
path = debug;
sourceTree = "<group>";
};
B4FCD56C2B603A6300832859 /* bdx */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -1594,6 +1608,7 @@
037C3DAF2991BD4F00B7EEE2 /* DeviceControllerDelegateBridge.h in Headers */,
B4FCD5712B603A6300832859 /* DownloadLogCommand.h in Headers */,
037C3DC32991BD5100B7EEE2 /* Commands.h in Headers */,
B4F773CA2CB54B61008C6B23 /* LeakChecker.h in Headers */,
037C3DB82991BD5000B7EEE2 /* ClusterCommandBridge.h in Headers */,
037C3DC82991BD5100B7EEE2 /* CHIPToolKeypair.h in Headers */,
037C3DB52991BD5000B7EEE2 /* WriteAttributeCommandBridge.h in Headers */,
Expand Down Expand Up @@ -1900,6 +1915,7 @@
03F430A82994112B00166449 /* editline.c in Sources */,
03F430AA2994113500166449 /* sysunix.c in Sources */,
B45373BF2A9FEA9100807602 /* adopt.c in Sources */,
B4F773CB2CB54B61008C6B23 /* LeakChecker.mm in Sources */,
B45373D12A9FEB0C00807602 /* alloc.c in Sources */,
B45373DD2A9FEB5300807602 /* base64-decode.c in Sources */,
B45373D22A9FEB0C00807602 /* buflist.c in Sources */,
Expand Down

0 comments on commit bd8803a

Please sign in to comment.