Skip to content

Commit bd8803a

Browse files
authored
[darwin-framework-tool] Automatically check for leaks on shutdown if enable_leak_checking is true (project-chip#35936)
1 parent 6034c87 commit bd8803a

File tree

5 files changed

+126
-1
lines changed

5 files changed

+126
-1
lines changed

examples/darwin-framework-tool/BUILD.gn

+8
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ declare_args() {
4343

4444
# Disable generating compiler database by default
4545
generate_compilation_database = false
46+
47+
# Enable automatic leak checks before the application exits
48+
enable_leak_checking = false
4649
}
4750

4851
sdk = "macosx"
@@ -219,6 +222,7 @@ executable("darwin-framework-tool") {
219222
"commands/provider/OTASoftwareUpdateInteractive.mm",
220223
"commands/storage/Commands.h",
221224
"commands/storage/StorageManagementCommand.mm",
225+
"debug/LeakChecker.mm",
222226
"logging/logging.mm",
223227
"main.mm",
224228
]
@@ -280,6 +284,10 @@ executable("darwin-framework-tool") {
280284
defines += [ "MTR_ENABLE_PROVISIONAL=1" ]
281285
}
282286

287+
if (enable_leak_checking) {
288+
defines += [ "DFT_ENABLE_LEAK_CHECKING=1" ]
289+
}
290+
283291
public_configs = [ ":config" ]
284292

285293
output_dir = root_out_dir
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (c) 2024 Project CHIP Authors
3+
* All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
#pragma once
20+
21+
/*
22+
* This function performs a memory leak check if the build flag `enable_leak_checking` is set to true
23+
* If leaks are detected, it overrides the provided exit code with `EXIT_FAILURE`.
24+
*
25+
* @param exitCode The initial exit code to return if no leaks are detected or if leak checking is disabled.
26+
* @return `EXIT_FAILURE` if leaks are detected and leak checking is enabled; otherwise, the original `exitCode`.
27+
*/
28+
int ConditionalLeaksCheck(int exitCode);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (c) 2024 Project CHIP Authors
3+
* All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
#include "LeakChecker.h"
20+
21+
#import <Foundation/Foundation.h>
22+
#include <unistd.h> // For getpid()
23+
24+
@interface LeakChecker : NSObject
25+
- (BOOL)hasMemoryLeaks;
26+
@end
27+
28+
@implementation LeakChecker
29+
30+
- (BOOL)hasMemoryLeaks
31+
{
32+
pid_t pid = getpid();
33+
auto * pidString = [NSString stringWithFormat:@"%d", pid];
34+
35+
auto * task = [[NSTask alloc] init];
36+
task.launchPath = @"/usr/bin/leaks";
37+
task.arguments = @[ pidString ];
38+
39+
auto * pipe = [NSPipe pipe];
40+
task.standardOutput = pipe;
41+
task.standardError = pipe;
42+
43+
NSFileHandle * fileHandle = [pipe fileHandleForReading];
44+
[task launch];
45+
[task waitUntilExit];
46+
47+
int exitCode = [task terminationStatus];
48+
if (exitCode) {
49+
NSData * data = [fileHandle readDataToEndOfFile];
50+
NSString * output = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
51+
NSLog(@"%@", output);
52+
return YES;
53+
}
54+
55+
return NO;
56+
}
57+
58+
@end
59+
60+
int ConditionalLeaksCheck(int exitCode)
61+
{
62+
#ifdef DFT_ENABLE_LEAK_CHECKING
63+
auto * leakChecker = [[LeakChecker alloc] init];
64+
if ([leakChecker hasMemoryLeaks]) {
65+
return EXIT_FAILURE;
66+
}
67+
#endif // DFT_ENABLE_LEAK_CHECKING
68+
69+
return exitCode;
70+
}

examples/darwin-framework-tool/main.mm

+4-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#import <Matter/Matter.h>
2020

21+
#import "debug/LeakChecker.h"
2122
#import "logging/logging.h"
2223

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

3637
int main(int argc, const char * argv[])
3738
{
39+
int exitCode = EXIT_SUCCESS;
3840
@autoreleasepool {
3941
dft::logging::Setup();
4042

@@ -49,6 +51,7 @@ int main(int argc, const char * argv[])
4951
registerCommandsStorage(commands);
5052
registerCommandsConfiguration(commands);
5153
registerClusters(commands);
52-
return commands.Run(argc, (char **) argv);
54+
exitCode = commands.Run(argc, (char **) argv);
5355
}
56+
return ConditionalLeaksCheck(exitCode);
5457
}

src/darwin/Framework/Matter.xcodeproj/project.pbxproj

+16
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,8 @@
373373
B4E262172AA0CF2000DBA5BC /* RemoteDataModelLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = B4E262132AA0C7A300DBA5BC /* RemoteDataModelLogger.h */; };
374374
B4E2621B2AA0D02000DBA5BC /* SleepCommand.mm in Sources */ = {isa = PBXBuildFile; fileRef = B4E262192AA0D01D00DBA5BC /* SleepCommand.mm */; };
375375
B4E2621E2AA0D02D00DBA5BC /* WaitForCommissioneeCommand.mm in Sources */ = {isa = PBXBuildFile; fileRef = B4E2621C2AA0D02A00DBA5BC /* WaitForCommissioneeCommand.mm */; };
376+
B4F773CA2CB54B61008C6B23 /* LeakChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = B4F773C72CB54B61008C6B23 /* LeakChecker.h */; };
377+
B4F773CB2CB54B61008C6B23 /* LeakChecker.mm in Sources */ = {isa = PBXBuildFile; fileRef = B4F773C82CB54B61008C6B23 /* LeakChecker.mm */; };
376378
B4FCD56A2B5EDBD300832859 /* MTRDiagnosticLogsType.h in Headers */ = {isa = PBXBuildFile; fileRef = B4FCD5692B5EDBD300832859 /* MTRDiagnosticLogsType.h */; settings = {ATTRIBUTES = (Public, ); }; };
377379
B4FCD5702B603A6300832859 /* Commands.h in Headers */ = {isa = PBXBuildFile; fileRef = B4FCD56D2B603A6300832859 /* Commands.h */; };
378380
B4FCD5712B603A6300832859 /* DownloadLogCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = B4FCD56E2B603A6300832859 /* DownloadLogCommand.h */; };
@@ -818,6 +820,8 @@
818820
B4E262132AA0C7A300DBA5BC /* RemoteDataModelLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RemoteDataModelLogger.h; sourceTree = "<group>"; };
819821
B4E262192AA0D01D00DBA5BC /* SleepCommand.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SleepCommand.mm; sourceTree = "<group>"; };
820822
B4E2621C2AA0D02A00DBA5BC /* WaitForCommissioneeCommand.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WaitForCommissioneeCommand.mm; sourceTree = "<group>"; };
823+
B4F773C72CB54B61008C6B23 /* LeakChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LeakChecker.h; sourceTree = "<group>"; };
824+
B4F773C82CB54B61008C6B23 /* LeakChecker.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LeakChecker.mm; sourceTree = "<group>"; };
821825
B4FCD5692B5EDBD300832859 /* MTRDiagnosticLogsType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRDiagnosticLogsType.h; sourceTree = "<group>"; };
822826
B4FCD56D2B603A6300832859 /* Commands.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Commands.h; sourceTree = "<group>"; };
823827
B4FCD56E2B603A6300832859 /* DownloadLogCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DownloadLogCommand.h; sourceTree = "<group>"; };
@@ -874,6 +878,7 @@
874878
037C3CA82991A44B00B7EEE2 /* darwin-framework-tool */ = {
875879
isa = PBXGroup;
876880
children = (
881+
B4F773C92CB54B61008C6B23 /* debug */,
877882
039145E02993102B00257B3E /* main.mm */,
878883
03F430A52994100000166449 /* controller */,
879884
039547092992DB02006D42A8 /* editline */,
@@ -1543,6 +1548,15 @@
15431548
path = delay;
15441549
sourceTree = "<group>";
15451550
};
1551+
B4F773C92CB54B61008C6B23 /* debug */ = {
1552+
isa = PBXGroup;
1553+
children = (
1554+
B4F773C72CB54B61008C6B23 /* LeakChecker.h */,
1555+
B4F773C82CB54B61008C6B23 /* LeakChecker.mm */,
1556+
);
1557+
path = debug;
1558+
sourceTree = "<group>";
1559+
};
15461560
B4FCD56C2B603A6300832859 /* bdx */ = {
15471561
isa = PBXGroup;
15481562
children = (
@@ -1594,6 +1608,7 @@
15941608
037C3DAF2991BD4F00B7EEE2 /* DeviceControllerDelegateBridge.h in Headers */,
15951609
B4FCD5712B603A6300832859 /* DownloadLogCommand.h in Headers */,
15961610
037C3DC32991BD5100B7EEE2 /* Commands.h in Headers */,
1611+
B4F773CA2CB54B61008C6B23 /* LeakChecker.h in Headers */,
15971612
037C3DB82991BD5000B7EEE2 /* ClusterCommandBridge.h in Headers */,
15981613
037C3DC82991BD5100B7EEE2 /* CHIPToolKeypair.h in Headers */,
15991614
037C3DB52991BD5000B7EEE2 /* WriteAttributeCommandBridge.h in Headers */,
@@ -1900,6 +1915,7 @@
19001915
03F430A82994112B00166449 /* editline.c in Sources */,
19011916
03F430AA2994113500166449 /* sysunix.c in Sources */,
19021917
B45373BF2A9FEA9100807602 /* adopt.c in Sources */,
1918+
B4F773CB2CB54B61008C6B23 /* LeakChecker.mm in Sources */,
19031919
B45373D12A9FEB0C00807602 /* alloc.c in Sources */,
19041920
B45373DD2A9FEB5300807602 /* base64-decode.c in Sources */,
19051921
B45373D22A9FEB0C00807602 /* buflist.c in Sources */,

0 commit comments

Comments
 (0)