aboutsummaryrefslogtreecommitdiff
path: root/clang/test/Analysis
diff options
context:
space:
mode:
Diffstat (limited to 'clang/test/Analysis')
-rw-r--r--clang/test/Analysis/Checkers/WebKit/objc-mock-types.h16
-rw-r--r--clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm9
-rw-r--r--clang/test/Analysis/Inputs/system-header-simulator-for-protobuf.h18
-rw-r--r--clang/test/Analysis/LifetimeSafety/benchmark.py82
-rw-r--r--clang/test/Analysis/NewDeleteLeaks.cpp33
5 files changed, 149 insertions, 9 deletions
diff --git a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
index dacb713..a5fc3d7 100644
--- a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
@@ -178,6 +178,22 @@ __attribute__((objc_root_class))
+ (NSNumber *)numberWithBool:(BOOL)value;
@end
+@interface NSResponder : NSObject
+@end
+
+@interface NSApplication : NSResponder
+
+extern NSApplication * NSApp;
+
+@property (class, readonly, strong) NSApplication *sharedApplication;
+
+- (void)finishLaunching;
+- (void)run;
+- (void)stop:(id)sender;
+- (void)terminate:(id)sender;
+
+@end
+
@interface SomeObj : NSObject
- (instancetype)_init;
- (SomeObj *)mutableCopy;
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
index c9d2fe8..a517dbc 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
@@ -398,12 +398,18 @@ namespace call_with_cf_constant {
void baz(const NSDictionary *);
void boo(NSNumber *);
void boo(CFTypeRef);
- void foo() {
+
+ struct Details {
+ int value;
+ };
+
+ void foo(Details* details) {
CFArrayCreateMutable(kCFAllocatorDefault, 10);
bar(@[@"hello"]);
baz(@{@"hello": @3});
boo(@YES);
boo(@NO);
+ boo(@(details->value));
}
}
@@ -582,6 +588,7 @@ struct Derived : Base {
[self doWork:@"hello", RetainPtr<SomeObj> { provide() }.get(), RetainPtr<CFMutableArrayRef> { provide_cf() }.get(), OSObjectPtr { provide_dispatch() }.get()];
[self doWork:__null];
[self doWork:nil];
+ [NSApp run];
}
- (SomeObj *)getSomeObj {
diff --git a/clang/test/Analysis/Inputs/system-header-simulator-for-protobuf.h b/clang/test/Analysis/Inputs/system-header-simulator-for-protobuf.h
new file mode 100644
index 0000000..cb12b55
--- /dev/null
+++ b/clang/test/Analysis/Inputs/system-header-simulator-for-protobuf.h
@@ -0,0 +1,18 @@
+// Like the compiler, the static analyzer treats some functions differently if
+// they come from a system header -- for example, it is assumed that system
+// functions do not arbitrarily free() their parameters, and that some bugs
+// found in system headers cannot be fixed by the user and should be
+// suppressed.
+#pragma clang system_header
+
+class Arena;
+class MessageLite {
+ int SomeArbitraryField;
+};
+
+// Originally declared in generated_message_util.h
+MessageLite *GetOwnedMessageInternal(Arena *, MessageLite *, Arena *);
+
+// Not a real protobuf function -- just introduced to validate that this file
+// is handled as a system header.
+void SomeOtherFunction(MessageLite *);
diff --git a/clang/test/Analysis/LifetimeSafety/benchmark.py b/clang/test/Analysis/LifetimeSafety/benchmark.py
index d2e5f0b..cd5b3081 100644
--- a/clang/test/Analysis/LifetimeSafety/benchmark.py
+++ b/clang/test/Analysis/LifetimeSafety/benchmark.py
@@ -145,6 +145,60 @@ def generate_cpp_nested_loop_test(n: int) -> str:
return cpp_code
+def generate_cpp_switch_fan_out_test(n: int) -> str:
+ """
+ Generates C++ code with a switch statement with N branches.
+ Each branch 'i' 'uses' (reads) a single, unique pointer 'pi'.
+ This pattern creates a "fan-in" join point for the backward
+ liveness analysis, stressing the LivenessMap::join operation
+ by forcing it to merge N disjoint, single-element sets of live origins.
+ The resulting complexity for LiveOrigins should be O(n log n) or higher.
+
+ Example (n=3):
+ struct MyObj { int id; ~MyObj() {} };
+
+ void switch_fan_out_3(int condition) {
+ MyObj v1{1}; MyObj v2{1}; MyObj v3{1};
+ MyObj* p1 = &v1; MyObj* p2 = &v2; MyObj* p3 = &v3;
+
+ switch (condition % 3) {
+ case 0:
+ p1->id = 1;
+ break;
+ case 1:
+ p2->id = 1;
+ break;
+ case 2:
+ p3->id = 1;
+ break;
+ }
+ }
+ """
+ if n <= 0:
+ return "// Number of variables must be positive."
+
+ cpp_code = "struct MyObj { int id; ~MyObj() {} };\n\n"
+ cpp_code += f"void switch_fan_out{n}(int condition) {{\n"
+ # Generate N distinct objects
+ for i in range(1, n + 1):
+ cpp_code += f" MyObj v{i}{{1}};\n"
+ cpp_code += "\n"
+ # Generate N distinct pointers, each as a separate variable
+ for i in range(1, n + 1):
+ cpp_code += f" MyObj* p{i} = &v{i};\n"
+ cpp_code += "\n"
+
+ cpp_code += f" switch (condition % {n}) {{\n"
+ for case_num in range(n):
+ cpp_code += f" case {case_num}:\n"
+ cpp_code += f" p{case_num + 1}->id = 1;\n"
+ cpp_code += " break;\n"
+
+ cpp_code += " }\n}\n"
+ cpp_code += f"\nint main() {{ switch_fan_out{n}(0); return 0; }}\n"
+ return cpp_code
+
+
def analyze_trace_file(trace_path: str) -> dict:
"""
Parses the -ftime-trace JSON output to find durations for the lifetime
@@ -156,14 +210,14 @@ def analyze_trace_file(trace_path: str) -> dict:
"total_us": 0.0,
"fact_gen_us": 0.0,
"loan_prop_us": 0.0,
- "expired_loans_us": 0.0,
+ "live_origins_us": 0.0,
}
event_name_map = {
"LifetimeSafetyAnalysis": "lifetime_us",
"ExecuteCompiler": "total_us",
"FactGenerator": "fact_gen_us",
"LoanPropagation": "loan_prop_us",
- "ExpiredLoans": "expired_loans_us",
+ "LiveOrigins": "live_origins_us",
}
try:
with open(trace_path, "r") as f:
@@ -227,7 +281,7 @@ def generate_markdown_report(results: dict) -> str:
# Table header
report.append(
- "| N (Input Size) | Total Time | Analysis Time (%) | Fact Generator (%) | Loan Propagation (%) | Expired Loans (%) |"
+ "| N (Input Size) | Total Time | Analysis Time (%) | Fact Generator (%) | Loan Propagation (%) | Live Origins (%) |"
)
report.append(
"|:---------------|-----------:|------------------:|-------------------:|---------------------:|------------------:|"
@@ -247,7 +301,7 @@ def generate_markdown_report(results: dict) -> str:
f"{data['lifetime_ms'][i] / total_t * 100:>17.2f}% |",
f"{data['fact_gen_ms'][i] / total_t * 100:>18.2f}% |",
f"{data['loan_prop_ms'][i] / total_t * 100:>20.2f}% |",
- f"{data['expired_loans_ms'][i] / total_t * 100:>17.2f}% |",
+ f"{data['live_origins_ms'][i] / total_t * 100:>17.2f}% |",
]
report.append(" ".join(row))
@@ -259,7 +313,7 @@ def generate_markdown_report(results: dict) -> str:
"Total Analysis": data["lifetime_ms"],
"FactGenerator": data["fact_gen_ms"],
"LoanPropagation": data["loan_prop_ms"],
- "ExpiredLoans": data["expired_loans_ms"],
+ "LiveOrigins": data["live_origins_ms"],
}
for phase_name, y_data in analysis_phases.items():
@@ -302,7 +356,13 @@ def run_single_test(
source_file,
]
- result = subprocess.run(clang_command, capture_output=True, text=True, timeout=60)
+ try:
+ result = subprocess.run(
+ clang_command, capture_output=True, text=True, timeout=60
+ )
+ except subprocess.TimeoutExpired:
+ print(f"Compilation timed out for N={n}!", file=sys.stderr)
+ return {}
if result.returncode != 0:
print(f"Compilation failed for N={n}!", file=sys.stderr)
@@ -354,6 +414,12 @@ if __name__ == "__main__":
"generator_func": generate_cpp_nested_loop_test,
"n_values": [50, 100, 150, 200],
},
+ {
+ "name": "switch_fan_out",
+ "title": "Switch Fan-out",
+ "generator_func": generate_cpp_switch_fan_out_test,
+ "n_values": [500, 1000, 2000, 4000],
+ },
]
results = {}
@@ -368,7 +434,7 @@ if __name__ == "__main__":
"total_ms": [],
"fact_gen_ms": [],
"loan_prop_ms": [],
- "expired_loans_ms": [],
+ "live_origins_ms": [],
}
for n in config["n_values"]:
durations_ms = run_single_test(
@@ -387,7 +453,7 @@ if __name__ == "__main__":
f" Total Analysis: {human_readable_time(durations_ms['lifetime_ms'])} | "
f"FactGen: {human_readable_time(durations_ms['fact_gen_ms'])} | "
f"LoanProp: {human_readable_time(durations_ms['loan_prop_ms'])} | "
- f"ExpiredLoans: {human_readable_time(durations_ms['expired_loans_ms'])}"
+ f"LiveOrigins: {human_readable_time(durations_ms['live_origins_ms'])}"
)
print("\n\n" + "=" * 80)
diff --git a/clang/test/Analysis/NewDeleteLeaks.cpp b/clang/test/Analysis/NewDeleteLeaks.cpp
index b2bad7e..d9c4b77 100644
--- a/clang/test/Analysis/NewDeleteLeaks.cpp
+++ b/clang/test/Analysis/NewDeleteLeaks.cpp
@@ -13,6 +13,8 @@
// RUN: unix.DynamicMemoryModeling:AddNoOwnershipChangeNotes=true
#include "Inputs/system-header-simulator-for-malloc.h"
+// For the tests in namespace protobuf_leak:
+#include "Inputs/system-header-simulator-for-protobuf.h"
//===----------------------------------------------------------------------===//
// Report for which we expect NoOwnershipChangeVisitor to add a new note.
@@ -218,3 +220,34 @@ void caller() {
(void)n;
} // no-warning: No potential memory leak here, because that's been already reported.
} // namespace symbol_reaper_lifetime
+
+// Check that we do not report false positives in automatically generated
+// protobuf code that passes dynamically allocated memory to a certain function
+// named GetOwnedMessageInternal.
+namespace protobuf_leak {
+Arena *some_arena, *some_submessage_arena;
+
+MessageLite *protobuf_leak() {
+ MessageLite *p = new MessageLite(); // Real protobuf code instantiates a
+ // subclass of MessageLite, but that's
+ // not relevant for the bug.
+ MessageLite *q = GetOwnedMessageInternal(some_arena, p, some_submessage_arena);
+ return q;
+ // No leak at end of function -- the pointer escapes in GetOwnedMessageInternal.
+}
+
+void validate_system_header() {
+ // The case protobuf_leak would also pass if GetOwnedMessageInternal wasn't
+ // declared in a system header. This test verifies that another function
+ // declared in the same header behaves differently (doesn't escape memory) to
+ // demonstrate that GetOwnedMessageInternal is indeed explicitly recognized
+ // by the analyzer.
+
+ // expected-note@+1 {{Memory is allocated}}
+ MessageLite *p = new MessageLite();
+ SomeOtherFunction(p);
+ // expected-warning@+2 {{Potential leak of memory pointed to by 'p'}}
+ // expected-note@+1 {{Potential leak of memory pointed to by 'p'}}
+}
+
+} // namespace protobuf_leak