[darwin-framework-tool] Automatically check for leaks on shutdown if enable_leak_checking is true (#35936)
diff --git a/examples/darwin-framework-tool/BUILD.gn b/examples/darwin-framework-tool/BUILD.gn
index 665dccf..2c20247 100644
--- a/examples/darwin-framework-tool/BUILD.gn
+++ b/examples/darwin-framework-tool/BUILD.gn
@@ -43,6 +43,9 @@
# Disable generating compiler database by default
generate_compilation_database = false
+
+ # Enable automatic leak checks before the application exits
+ enable_leak_checking = false
}
sdk = "macosx"
@@ -219,6 +222,7 @@
"commands/provider/OTASoftwareUpdateInteractive.mm",
"commands/storage/Commands.h",
"commands/storage/StorageManagementCommand.mm",
+ "debug/LeakChecker.mm",
"logging/logging.mm",
"main.mm",
]
@@ -280,6 +284,10 @@
defines += [ "MTR_ENABLE_PROVISIONAL=1" ]
}
+ if (enable_leak_checking) {
+ defines += [ "DFT_ENABLE_LEAK_CHECKING=1" ]
+ }
+
public_configs = [ ":config" ]
output_dir = root_out_dir
diff --git a/examples/darwin-framework-tool/debug/LeakChecker.h b/examples/darwin-framework-tool/debug/LeakChecker.h
new file mode 100644
index 0000000..c83ed64
--- /dev/null
+++ b/examples/darwin-framework-tool/debug/LeakChecker.h
@@ -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);
diff --git a/examples/darwin-framework-tool/debug/LeakChecker.mm b/examples/darwin-framework-tool/debug/LeakChecker.mm
new file mode 100644
index 0000000..72a4414
--- /dev/null
+++ b/examples/darwin-framework-tool/debug/LeakChecker.mm
@@ -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;
+}
diff --git a/examples/darwin-framework-tool/main.mm b/examples/darwin-framework-tool/main.mm
index 5f31cb6..ad31cfe 100644
--- a/examples/darwin-framework-tool/main.mm
+++ b/examples/darwin-framework-tool/main.mm
@@ -18,6 +18,7 @@
#import <Matter/Matter.h>
+#import "debug/LeakChecker.h"
#import "logging/logging.h"
#include "commands/bdx/Commands.h"
@@ -35,6 +36,7 @@
int main(int argc, const char * argv[])
{
+ int exitCode = EXIT_SUCCESS;
@autoreleasepool {
dft::logging::Setup();
@@ -49,6 +51,7 @@
registerCommandsStorage(commands);
registerCommandsConfiguration(commands);
registerClusters(commands);
- return commands.Run(argc, (char **) argv);
+ exitCode = commands.Run(argc, (char **) argv);
}
+ return ConditionalLeaksCheck(exitCode);
}
diff --git a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj
index afc1df1..8a39c63 100644
--- a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj
+++ b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj
@@ -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 */; };
@@ -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>"; };
@@ -874,6 +878,7 @@
037C3CA82991A44B00B7EEE2 /* darwin-framework-tool */ = {
isa = PBXGroup;
children = (
+ B4F773C92CB54B61008C6B23 /* debug */,
039145E02993102B00257B3E /* main.mm */,
03F430A52994100000166449 /* controller */,
039547092992DB02006D42A8 /* editline */,
@@ -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 = (
@@ -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 */,
@@ -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 */,