/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/InstrumentationRuntime/TSan/InstrumentationRuntimeTSan.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- InstrumentationRuntimeTSan.cpp ------------------------------------===// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | |
9 | | #include "InstrumentationRuntimeTSan.h" |
10 | | |
11 | | #include "Plugins/Process/Utility/HistoryThread.h" |
12 | | #include "lldb/Breakpoint/StoppointCallbackContext.h" |
13 | | #include "lldb/Core/Debugger.h" |
14 | | #include "lldb/Core/Module.h" |
15 | | #include "lldb/Core/PluginInterface.h" |
16 | | #include "lldb/Core/PluginManager.h" |
17 | | #include "lldb/Core/ValueObject.h" |
18 | | #include "lldb/Expression/UserExpression.h" |
19 | | #include "lldb/Host/StreamFile.h" |
20 | | #include "lldb/Interpreter/CommandReturnObject.h" |
21 | | #include "lldb/Symbol/Symbol.h" |
22 | | #include "lldb/Symbol/SymbolContext.h" |
23 | | #include "lldb/Symbol/Variable.h" |
24 | | #include "lldb/Symbol/VariableList.h" |
25 | | #include "lldb/Target/InstrumentationRuntimeStopInfo.h" |
26 | | #include "lldb/Target/SectionLoadList.h" |
27 | | #include "lldb/Target/StopInfo.h" |
28 | | #include "lldb/Target/Target.h" |
29 | | #include "lldb/Target/Thread.h" |
30 | | #include "lldb/Utility/RegularExpression.h" |
31 | | #include "lldb/Utility/Stream.h" |
32 | | |
33 | | #include <memory> |
34 | | |
35 | | using namespace lldb; |
36 | | using namespace lldb_private; |
37 | | |
38 | | LLDB_PLUGIN_DEFINE(InstrumentationRuntimeTSan) |
39 | | |
40 | | lldb::InstrumentationRuntimeSP |
41 | 2.25k | InstrumentationRuntimeTSan::CreateInstance(const lldb::ProcessSP &process_sp) { |
42 | 2.25k | return InstrumentationRuntimeSP(new InstrumentationRuntimeTSan(process_sp)); |
43 | 2.25k | } |
44 | | |
45 | 3.92k | void InstrumentationRuntimeTSan::Initialize() { |
46 | 3.92k | PluginManager::RegisterPlugin( |
47 | 3.92k | GetPluginNameStatic(), "ThreadSanitizer instrumentation runtime plugin.", |
48 | 3.92k | CreateInstance, GetTypeStatic); |
49 | 3.92k | } |
50 | | |
51 | 3.92k | void InstrumentationRuntimeTSan::Terminate() { |
52 | 3.92k | PluginManager::UnregisterPlugin(CreateInstance); |
53 | 3.92k | } |
54 | | |
55 | 9.25k | lldb::InstrumentationRuntimeType InstrumentationRuntimeTSan::GetTypeStatic() { |
56 | 9.25k | return eInstrumentationRuntimeTypeThreadSanitizer; |
57 | 9.25k | } |
58 | | |
59 | 2.25k | InstrumentationRuntimeTSan::~InstrumentationRuntimeTSan() { Deactivate(); } |
60 | | |
61 | | const char *thread_sanitizer_retrieve_report_data_prefix = R"( |
62 | | extern "C" |
63 | | { |
64 | | void *__tsan_get_current_report(); |
65 | | int __tsan_get_report_data(void *report, const char **description, int *count, |
66 | | int *stack_count, int *mop_count, int *loc_count, |
67 | | int *mutex_count, int *thread_count, |
68 | | int *unique_tid_count, void **sleep_trace, |
69 | | unsigned long trace_size); |
70 | | int __tsan_get_report_stack(void *report, unsigned long idx, void **trace, |
71 | | unsigned long trace_size); |
72 | | int __tsan_get_report_mop(void *report, unsigned long idx, int *tid, void **addr, |
73 | | int *size, int *write, int *atomic, void **trace, |
74 | | unsigned long trace_size); |
75 | | int __tsan_get_report_loc(void *report, unsigned long idx, const char **type, |
76 | | void **addr, unsigned long *start, unsigned long *size, int *tid, |
77 | | int *fd, int *suppressable, void **trace, |
78 | | unsigned long trace_size); |
79 | | int __tsan_get_report_mutex(void *report, unsigned long idx, unsigned long *mutex_id, void **addr, |
80 | | int *destroyed, void **trace, unsigned long trace_size); |
81 | | int __tsan_get_report_thread(void *report, unsigned long idx, int *tid, unsigned long *os_id, |
82 | | int *running, const char **name, int *parent_tid, |
83 | | void **trace, unsigned long trace_size); |
84 | | int __tsan_get_report_unique_tid(void *report, unsigned long idx, int *tid); |
85 | | |
86 | | // TODO: dlsym won't work on Windows. |
87 | | void *dlsym(void* handle, const char* symbol); |
88 | | int (*ptr__tsan_get_report_loc_object_type)(void *report, unsigned long idx, const char **object_type); |
89 | | } |
90 | | )"; |
91 | | |
92 | | const char *thread_sanitizer_retrieve_report_data_command = R"( |
93 | | |
94 | | const int REPORT_TRACE_SIZE = 128; |
95 | | const int REPORT_ARRAY_SIZE = 4; |
96 | | |
97 | | struct { |
98 | | void *report; |
99 | | const char *description; |
100 | | int report_count; |
101 | | |
102 | | void *sleep_trace[REPORT_TRACE_SIZE]; |
103 | | |
104 | | int stack_count; |
105 | | struct { |
106 | | int idx; |
107 | | void *trace[REPORT_TRACE_SIZE]; |
108 | | } stacks[REPORT_ARRAY_SIZE]; |
109 | | |
110 | | int mop_count; |
111 | | struct { |
112 | | int idx; |
113 | | int tid; |
114 | | int size; |
115 | | int write; |
116 | | int atomic; |
117 | | void *addr; |
118 | | void *trace[REPORT_TRACE_SIZE]; |
119 | | } mops[REPORT_ARRAY_SIZE]; |
120 | | |
121 | | int loc_count; |
122 | | struct { |
123 | | int idx; |
124 | | const char *type; |
125 | | void *addr; |
126 | | unsigned long start; |
127 | | unsigned long size; |
128 | | int tid; |
129 | | int fd; |
130 | | int suppressable; |
131 | | void *trace[REPORT_TRACE_SIZE]; |
132 | | const char *object_type; |
133 | | } locs[REPORT_ARRAY_SIZE]; |
134 | | |
135 | | int mutex_count; |
136 | | struct { |
137 | | int idx; |
138 | | unsigned long mutex_id; |
139 | | void *addr; |
140 | | int destroyed; |
141 | | void *trace[REPORT_TRACE_SIZE]; |
142 | | } mutexes[REPORT_ARRAY_SIZE]; |
143 | | |
144 | | int thread_count; |
145 | | struct { |
146 | | int idx; |
147 | | int tid; |
148 | | unsigned long os_id; |
149 | | int running; |
150 | | const char *name; |
151 | | int parent_tid; |
152 | | void *trace[REPORT_TRACE_SIZE]; |
153 | | } threads[REPORT_ARRAY_SIZE]; |
154 | | |
155 | | int unique_tid_count; |
156 | | struct { |
157 | | int idx; |
158 | | int tid; |
159 | | } unique_tids[REPORT_ARRAY_SIZE]; |
160 | | } t = {0}; |
161 | | |
162 | | ptr__tsan_get_report_loc_object_type = (typeof(ptr__tsan_get_report_loc_object_type))(void *)dlsym((void*)-2 /*RTLD_DEFAULT*/, "__tsan_get_report_loc_object_type"); |
163 | | |
164 | | t.report = __tsan_get_current_report(); |
165 | | __tsan_get_report_data(t.report, &t.description, &t.report_count, &t.stack_count, &t.mop_count, &t.loc_count, &t.mutex_count, &t.thread_count, &t.unique_tid_count, t.sleep_trace, REPORT_TRACE_SIZE); |
166 | | |
167 | | if (t.stack_count > REPORT_ARRAY_SIZE) t.stack_count = REPORT_ARRAY_SIZE; |
168 | | for (int i = 0; i < t.stack_count; i++) { |
169 | | t.stacks[i].idx = i; |
170 | | __tsan_get_report_stack(t.report, i, t.stacks[i].trace, REPORT_TRACE_SIZE); |
171 | | } |
172 | | |
173 | | if (t.mop_count > REPORT_ARRAY_SIZE) t.mop_count = REPORT_ARRAY_SIZE; |
174 | | for (int i = 0; i < t.mop_count; i++) { |
175 | | t.mops[i].idx = i; |
176 | | __tsan_get_report_mop(t.report, i, &t.mops[i].tid, &t.mops[i].addr, &t.mops[i].size, &t.mops[i].write, &t.mops[i].atomic, t.mops[i].trace, REPORT_TRACE_SIZE); |
177 | | } |
178 | | |
179 | | if (t.loc_count > REPORT_ARRAY_SIZE) t.loc_count = REPORT_ARRAY_SIZE; |
180 | | for (int i = 0; i < t.loc_count; i++) { |
181 | | t.locs[i].idx = i; |
182 | | __tsan_get_report_loc(t.report, i, &t.locs[i].type, &t.locs[i].addr, &t.locs[i].start, &t.locs[i].size, &t.locs[i].tid, &t.locs[i].fd, &t.locs[i].suppressable, t.locs[i].trace, REPORT_TRACE_SIZE); |
183 | | if (ptr__tsan_get_report_loc_object_type) |
184 | | ptr__tsan_get_report_loc_object_type(t.report, i, &t.locs[i].object_type); |
185 | | } |
186 | | |
187 | | if (t.mutex_count > REPORT_ARRAY_SIZE) t.mutex_count = REPORT_ARRAY_SIZE; |
188 | | for (int i = 0; i < t.mutex_count; i++) { |
189 | | t.mutexes[i].idx = i; |
190 | | __tsan_get_report_mutex(t.report, i, &t.mutexes[i].mutex_id, &t.mutexes[i].addr, &t.mutexes[i].destroyed, t.mutexes[i].trace, REPORT_TRACE_SIZE); |
191 | | } |
192 | | |
193 | | if (t.thread_count > REPORT_ARRAY_SIZE) t.thread_count = REPORT_ARRAY_SIZE; |
194 | | for (int i = 0; i < t.thread_count; i++) { |
195 | | t.threads[i].idx = i; |
196 | | __tsan_get_report_thread(t.report, i, &t.threads[i].tid, &t.threads[i].os_id, &t.threads[i].running, &t.threads[i].name, &t.threads[i].parent_tid, t.threads[i].trace, REPORT_TRACE_SIZE); |
197 | | } |
198 | | |
199 | | if (t.unique_tid_count > REPORT_ARRAY_SIZE) t.unique_tid_count = REPORT_ARRAY_SIZE; |
200 | | for (int i = 0; i < t.unique_tid_count; i++) { |
201 | | t.unique_tids[i].idx = i; |
202 | | __tsan_get_report_unique_tid(t.report, i, &t.unique_tids[i].tid); |
203 | | } |
204 | | |
205 | | t; |
206 | | )"; |
207 | | |
208 | | static StructuredData::ArraySP |
209 | | CreateStackTrace(ValueObjectSP o, |
210 | 0 | const std::string &trace_item_name = ".trace") { |
211 | 0 | auto trace_sp = std::make_shared<StructuredData::Array>(); |
212 | 0 | ValueObjectSP trace_value_object = |
213 | 0 | o->GetValueForExpressionPath(trace_item_name.c_str()); |
214 | 0 | size_t count = trace_value_object->GetNumChildren(); |
215 | 0 | for (size_t j = 0; j < count; j++) { |
216 | 0 | addr_t trace_addr = |
217 | 0 | trace_value_object->GetChildAtIndex(j)->GetValueAsUnsigned(0); |
218 | 0 | if (trace_addr == 0) |
219 | 0 | break; |
220 | 0 | trace_sp->AddIntegerItem(trace_addr); |
221 | 0 | } |
222 | 0 | return trace_sp; |
223 | 0 | } |
224 | | |
225 | | static StructuredData::ArraySP ConvertToStructuredArray( |
226 | | ValueObjectSP return_value_sp, const std::string &items_name, |
227 | | const std::string &count_name, |
228 | | std::function<void(const ValueObjectSP &o, |
229 | | const StructuredData::DictionarySP &dict)> const |
230 | 0 | &callback) { |
231 | 0 | auto array_sp = std::make_shared<StructuredData::Array>(); |
232 | 0 | unsigned int count = |
233 | 0 | return_value_sp->GetValueForExpressionPath(count_name.c_str()) |
234 | 0 | ->GetValueAsUnsigned(0); |
235 | 0 | ValueObjectSP objects = |
236 | 0 | return_value_sp->GetValueForExpressionPath(items_name.c_str()); |
237 | 0 | for (unsigned int i = 0; i < count; i++) { |
238 | 0 | ValueObjectSP o = objects->GetChildAtIndex(i); |
239 | 0 | auto dict_sp = std::make_shared<StructuredData::Dictionary>(); |
240 | |
|
241 | 0 | callback(o, dict_sp); |
242 | |
|
243 | 0 | array_sp->AddItem(dict_sp); |
244 | 0 | } |
245 | 0 | return array_sp; |
246 | 0 | } |
247 | | |
248 | | static std::string RetrieveString(ValueObjectSP return_value_sp, |
249 | | ProcessSP process_sp, |
250 | 0 | const std::string &expression_path) { |
251 | 0 | addr_t ptr = |
252 | 0 | return_value_sp->GetValueForExpressionPath(expression_path.c_str()) |
253 | 0 | ->GetValueAsUnsigned(0); |
254 | 0 | std::string str; |
255 | 0 | Status error; |
256 | 0 | process_sp->ReadCStringFromMemory(ptr, str, error); |
257 | 0 | return str; |
258 | 0 | } |
259 | | |
260 | | static void |
261 | | GetRenumberedThreadIds(ProcessSP process_sp, ValueObjectSP data, |
262 | 0 | std::map<uint64_t, user_id_t> &thread_id_map) { |
263 | 0 | ConvertToStructuredArray( |
264 | 0 | data, ".threads", ".thread_count", |
265 | 0 | [process_sp, &thread_id_map](const ValueObjectSP &o, |
266 | 0 | const StructuredData::DictionarySP &dict) { |
267 | 0 | uint64_t thread_id = |
268 | 0 | o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0); |
269 | 0 | uint64_t thread_os_id = |
270 | 0 | o->GetValueForExpressionPath(".os_id")->GetValueAsUnsigned(0); |
271 | 0 | user_id_t lldb_user_id = 0; |
272 | |
|
273 | 0 | bool can_update = true; |
274 | 0 | ThreadSP lldb_thread = process_sp->GetThreadList().FindThreadByID( |
275 | 0 | thread_os_id, can_update); |
276 | 0 | if (lldb_thread) { |
277 | 0 | lldb_user_id = lldb_thread->GetIndexID(); |
278 | 0 | } else { |
279 | | // This isn't a live thread anymore. Ask process to assign a new |
280 | | // Index ID (or return an old one if we've already seen this |
281 | | // thread_os_id). It will also make sure that no new threads are |
282 | | // assigned this Index ID. |
283 | 0 | lldb_user_id = process_sp->AssignIndexIDToThread(thread_os_id); |
284 | 0 | } |
285 | |
|
286 | 0 | thread_id_map[thread_id] = lldb_user_id; |
287 | 0 | }); |
288 | 0 | } |
289 | | |
290 | | static user_id_t Renumber(uint64_t id, |
291 | 0 | std::map<uint64_t, user_id_t> &thread_id_map) { |
292 | 0 | auto IT = thread_id_map.find(id); |
293 | 0 | if (IT == thread_id_map.end()) |
294 | 0 | return 0; |
295 | | |
296 | 0 | return IT->second; |
297 | 0 | } |
298 | | |
299 | | StructuredData::ObjectSP InstrumentationRuntimeTSan::RetrieveReportData( |
300 | 0 | ExecutionContextRef exe_ctx_ref) { |
301 | 0 | ProcessSP process_sp = GetProcessSP(); |
302 | 0 | if (!process_sp) |
303 | 0 | return StructuredData::ObjectSP(); |
304 | | |
305 | 0 | ThreadSP thread_sp = exe_ctx_ref.GetThreadSP(); |
306 | 0 | StackFrameSP frame_sp = |
307 | 0 | thread_sp->GetSelectedFrame(DoNoSelectMostRelevantFrame); |
308 | |
|
309 | 0 | if (!frame_sp) |
310 | 0 | return StructuredData::ObjectSP(); |
311 | | |
312 | 0 | EvaluateExpressionOptions options; |
313 | 0 | options.SetUnwindOnError(true); |
314 | 0 | options.SetTryAllThreads(true); |
315 | 0 | options.SetStopOthers(true); |
316 | 0 | options.SetIgnoreBreakpoints(true); |
317 | 0 | options.SetTimeout(process_sp->GetUtilityExpressionTimeout()); |
318 | 0 | options.SetPrefix(thread_sanitizer_retrieve_report_data_prefix); |
319 | 0 | options.SetAutoApplyFixIts(false); |
320 | 0 | options.SetLanguage(eLanguageTypeObjC_plus_plus); |
321 | |
|
322 | 0 | ValueObjectSP main_value; |
323 | 0 | ExecutionContext exe_ctx; |
324 | 0 | Status eval_error; |
325 | 0 | frame_sp->CalculateExecutionContext(exe_ctx); |
326 | 0 | ExpressionResults result = UserExpression::Evaluate( |
327 | 0 | exe_ctx, options, thread_sanitizer_retrieve_report_data_command, "", |
328 | 0 | main_value, eval_error); |
329 | 0 | if (result != eExpressionCompleted) { |
330 | 0 | StreamString ss; |
331 | 0 | ss << "cannot evaluate ThreadSanitizer expression:\n"; |
332 | 0 | ss << eval_error.AsCString(); |
333 | 0 | Debugger::ReportWarning(ss.GetString().str(), |
334 | 0 | process_sp->GetTarget().GetDebugger().GetID()); |
335 | 0 | return StructuredData::ObjectSP(); |
336 | 0 | } |
337 | | |
338 | 0 | std::map<uint64_t, user_id_t> thread_id_map; |
339 | 0 | GetRenumberedThreadIds(process_sp, main_value, thread_id_map); |
340 | |
|
341 | 0 | auto dict = std::make_shared<StructuredData::Dictionary>(); |
342 | 0 | dict->AddStringItem("instrumentation_class", "ThreadSanitizer"); |
343 | 0 | dict->AddStringItem("issue_type", |
344 | 0 | RetrieveString(main_value, process_sp, ".description")); |
345 | 0 | dict->AddIntegerItem("report_count", |
346 | 0 | main_value->GetValueForExpressionPath(".report_count") |
347 | 0 | ->GetValueAsUnsigned(0)); |
348 | 0 | dict->AddItem("sleep_trace", CreateStackTrace( |
349 | 0 | main_value, ".sleep_trace")); |
350 | |
|
351 | 0 | StructuredData::ArraySP stacks = ConvertToStructuredArray( |
352 | 0 | main_value, ".stacks", ".stack_count", |
353 | 0 | [thread_sp](const ValueObjectSP &o, |
354 | 0 | const StructuredData::DictionarySP &dict) { |
355 | 0 | dict->AddIntegerItem( |
356 | 0 | "index", |
357 | 0 | o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); |
358 | 0 | dict->AddItem("trace", CreateStackTrace(o)); |
359 | | // "stacks" happen on the current thread |
360 | 0 | dict->AddIntegerItem("thread_id", thread_sp->GetIndexID()); |
361 | 0 | }); |
362 | 0 | dict->AddItem("stacks", stacks); |
363 | |
|
364 | 0 | StructuredData::ArraySP mops = ConvertToStructuredArray( |
365 | 0 | main_value, ".mops", ".mop_count", |
366 | 0 | [&thread_id_map](const ValueObjectSP &o, |
367 | 0 | const StructuredData::DictionarySP &dict) { |
368 | 0 | dict->AddIntegerItem( |
369 | 0 | "index", |
370 | 0 | o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); |
371 | 0 | dict->AddIntegerItem( |
372 | 0 | "thread_id", |
373 | 0 | Renumber( |
374 | 0 | o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0), |
375 | 0 | thread_id_map)); |
376 | 0 | dict->AddIntegerItem( |
377 | 0 | "size", |
378 | 0 | o->GetValueForExpressionPath(".size")->GetValueAsUnsigned(0)); |
379 | 0 | dict->AddBooleanItem( |
380 | 0 | "is_write", |
381 | 0 | o->GetValueForExpressionPath(".write")->GetValueAsUnsigned(0)); |
382 | 0 | dict->AddBooleanItem( |
383 | 0 | "is_atomic", |
384 | 0 | o->GetValueForExpressionPath(".atomic")->GetValueAsUnsigned(0)); |
385 | 0 | dict->AddIntegerItem( |
386 | 0 | "address", |
387 | 0 | o->GetValueForExpressionPath(".addr")->GetValueAsUnsigned(0)); |
388 | 0 | dict->AddItem("trace", CreateStackTrace(o)); |
389 | 0 | }); |
390 | 0 | dict->AddItem("mops", mops); |
391 | |
|
392 | 0 | StructuredData::ArraySP locs = ConvertToStructuredArray( |
393 | 0 | main_value, ".locs", ".loc_count", |
394 | 0 | [process_sp, &thread_id_map](const ValueObjectSP &o, |
395 | 0 | const StructuredData::DictionarySP &dict) { |
396 | 0 | dict->AddIntegerItem( |
397 | 0 | "index", |
398 | 0 | o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); |
399 | 0 | dict->AddStringItem("type", RetrieveString(o, process_sp, ".type")); |
400 | 0 | dict->AddIntegerItem( |
401 | 0 | "address", |
402 | 0 | o->GetValueForExpressionPath(".addr")->GetValueAsUnsigned(0)); |
403 | 0 | dict->AddIntegerItem( |
404 | 0 | "start", |
405 | 0 | o->GetValueForExpressionPath(".start")->GetValueAsUnsigned(0)); |
406 | 0 | dict->AddIntegerItem( |
407 | 0 | "size", |
408 | 0 | o->GetValueForExpressionPath(".size")->GetValueAsUnsigned(0)); |
409 | 0 | dict->AddIntegerItem( |
410 | 0 | "thread_id", |
411 | 0 | Renumber( |
412 | 0 | o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0), |
413 | 0 | thread_id_map)); |
414 | 0 | dict->AddIntegerItem( |
415 | 0 | "file_descriptor", |
416 | 0 | o->GetValueForExpressionPath(".fd")->GetValueAsUnsigned(0)); |
417 | 0 | dict->AddIntegerItem("suppressable", |
418 | 0 | o->GetValueForExpressionPath(".suppressable") |
419 | 0 | ->GetValueAsUnsigned(0)); |
420 | 0 | dict->AddItem("trace", CreateStackTrace(o)); |
421 | 0 | dict->AddStringItem("object_type", |
422 | 0 | RetrieveString(o, process_sp, ".object_type")); |
423 | 0 | }); |
424 | 0 | dict->AddItem("locs", locs); |
425 | |
|
426 | 0 | StructuredData::ArraySP mutexes = ConvertToStructuredArray( |
427 | 0 | main_value, ".mutexes", ".mutex_count", |
428 | 0 | [](const ValueObjectSP &o, const StructuredData::DictionarySP &dict) { |
429 | 0 | dict->AddIntegerItem( |
430 | 0 | "index", |
431 | 0 | o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); |
432 | 0 | dict->AddIntegerItem( |
433 | 0 | "mutex_id", |
434 | 0 | o->GetValueForExpressionPath(".mutex_id")->GetValueAsUnsigned(0)); |
435 | 0 | dict->AddIntegerItem( |
436 | 0 | "address", |
437 | 0 | o->GetValueForExpressionPath(".addr")->GetValueAsUnsigned(0)); |
438 | 0 | dict->AddIntegerItem( |
439 | 0 | "destroyed", |
440 | 0 | o->GetValueForExpressionPath(".destroyed")->GetValueAsUnsigned(0)); |
441 | 0 | dict->AddItem("trace", CreateStackTrace(o)); |
442 | 0 | }); |
443 | 0 | dict->AddItem("mutexes", mutexes); |
444 | |
|
445 | 0 | StructuredData::ArraySP threads = ConvertToStructuredArray( |
446 | 0 | main_value, ".threads", ".thread_count", |
447 | 0 | [process_sp, &thread_id_map](const ValueObjectSP &o, |
448 | 0 | const StructuredData::DictionarySP &dict) { |
449 | 0 | dict->AddIntegerItem( |
450 | 0 | "index", |
451 | 0 | o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); |
452 | 0 | dict->AddIntegerItem( |
453 | 0 | "thread_id", |
454 | 0 | Renumber( |
455 | 0 | o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0), |
456 | 0 | thread_id_map)); |
457 | 0 | dict->AddIntegerItem( |
458 | 0 | "thread_os_id", |
459 | 0 | o->GetValueForExpressionPath(".os_id")->GetValueAsUnsigned(0)); |
460 | 0 | dict->AddIntegerItem( |
461 | 0 | "running", |
462 | 0 | o->GetValueForExpressionPath(".running")->GetValueAsUnsigned(0)); |
463 | 0 | dict->AddStringItem("name", RetrieveString(o, process_sp, ".name")); |
464 | 0 | dict->AddIntegerItem( |
465 | 0 | "parent_thread_id", |
466 | 0 | Renumber(o->GetValueForExpressionPath(".parent_tid") |
467 | 0 | ->GetValueAsUnsigned(0), |
468 | 0 | thread_id_map)); |
469 | 0 | dict->AddItem("trace", CreateStackTrace(o)); |
470 | 0 | }); |
471 | 0 | dict->AddItem("threads", threads); |
472 | |
|
473 | 0 | StructuredData::ArraySP unique_tids = ConvertToStructuredArray( |
474 | 0 | main_value, ".unique_tids", ".unique_tid_count", |
475 | 0 | [&thread_id_map](const ValueObjectSP &o, |
476 | 0 | const StructuredData::DictionarySP &dict) { |
477 | 0 | dict->AddIntegerItem( |
478 | 0 | "index", |
479 | 0 | o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); |
480 | 0 | dict->AddIntegerItem( |
481 | 0 | "tid", |
482 | 0 | Renumber( |
483 | 0 | o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0), |
484 | 0 | thread_id_map)); |
485 | 0 | }); |
486 | 0 | dict->AddItem("unique_tids", unique_tids); |
487 | |
|
488 | 0 | return dict; |
489 | 0 | } |
490 | | |
491 | | std::string |
492 | 0 | InstrumentationRuntimeTSan::FormatDescription(StructuredData::ObjectSP report) { |
493 | 0 | std::string description = std::string(report->GetAsDictionary() |
494 | 0 | ->GetValueForKey("issue_type") |
495 | 0 | ->GetAsString() |
496 | 0 | ->GetValue()); |
497 | |
|
498 | 0 | if (description == "data-race") { |
499 | 0 | return "Data race"; |
500 | 0 | } else if (description == "data-race-vptr") { |
501 | 0 | return "Data race on C++ virtual pointer"; |
502 | 0 | } else if (description == "heap-use-after-free") { |
503 | 0 | return "Use of deallocated memory"; |
504 | 0 | } else if (description == "heap-use-after-free-vptr") { |
505 | 0 | return "Use of deallocated C++ virtual pointer"; |
506 | 0 | } else if (description == "thread-leak") { |
507 | 0 | return "Thread leak"; |
508 | 0 | } else if (description == "locked-mutex-destroy") { |
509 | 0 | return "Destruction of a locked mutex"; |
510 | 0 | } else if (description == "mutex-double-lock") { |
511 | 0 | return "Double lock of a mutex"; |
512 | 0 | } else if (description == "mutex-invalid-access") { |
513 | 0 | return "Use of an uninitialized or destroyed mutex"; |
514 | 0 | } else if (description == "mutex-bad-unlock") { |
515 | 0 | return "Unlock of an unlocked mutex (or by a wrong thread)"; |
516 | 0 | } else if (description == "mutex-bad-read-lock") { |
517 | 0 | return "Read lock of a write locked mutex"; |
518 | 0 | } else if (description == "mutex-bad-read-unlock") { |
519 | 0 | return "Read unlock of a write locked mutex"; |
520 | 0 | } else if (description == "signal-unsafe-call") { |
521 | 0 | return "Signal-unsafe call inside a signal handler"; |
522 | 0 | } else if (description == "errno-in-signal-handler") { |
523 | 0 | return "Overwrite of errno in a signal handler"; |
524 | 0 | } else if (description == "lock-order-inversion") { |
525 | 0 | return "Lock order inversion (potential deadlock)"; |
526 | 0 | } else if (description == "external-race") { |
527 | 0 | return "Race on a library object"; |
528 | 0 | } else if (description == "swift-access-race") { |
529 | 0 | return "Swift access race"; |
530 | 0 | } |
531 | | |
532 | | // for unknown report codes just show the code |
533 | 0 | return description; |
534 | 0 | } |
535 | | |
536 | 0 | static std::string Sprintf(const char *format, ...) { |
537 | 0 | StreamString s; |
538 | 0 | va_list args; |
539 | 0 | va_start(args, format); |
540 | 0 | s.PrintfVarArg(format, args); |
541 | 0 | va_end(args); |
542 | 0 | return std::string(s.GetString()); |
543 | 0 | } |
544 | | |
545 | 0 | static std::string GetSymbolNameFromAddress(ProcessSP process_sp, addr_t addr) { |
546 | 0 | lldb_private::Address so_addr; |
547 | 0 | if (!process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress(addr, |
548 | 0 | so_addr)) |
549 | 0 | return ""; |
550 | | |
551 | 0 | lldb_private::Symbol *symbol = so_addr.CalculateSymbolContextSymbol(); |
552 | 0 | if (!symbol) |
553 | 0 | return ""; |
554 | | |
555 | 0 | std::string sym_name = symbol->GetName().GetCString(); |
556 | 0 | return sym_name; |
557 | 0 | } |
558 | | |
559 | | static void GetSymbolDeclarationFromAddress(ProcessSP process_sp, addr_t addr, |
560 | 0 | Declaration &decl) { |
561 | 0 | lldb_private::Address so_addr; |
562 | 0 | if (!process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress(addr, |
563 | 0 | so_addr)) |
564 | 0 | return; |
565 | | |
566 | 0 | lldb_private::Symbol *symbol = so_addr.CalculateSymbolContextSymbol(); |
567 | 0 | if (!symbol) |
568 | 0 | return; |
569 | | |
570 | 0 | ConstString sym_name = symbol->GetMangled().GetName(Mangled::ePreferMangled); |
571 | |
|
572 | 0 | ModuleSP module = symbol->CalculateSymbolContextModule(); |
573 | 0 | if (!module) |
574 | 0 | return; |
575 | | |
576 | 0 | VariableList var_list; |
577 | 0 | module->FindGlobalVariables(sym_name, CompilerDeclContext(), 1U, var_list); |
578 | 0 | if (var_list.GetSize() < 1) |
579 | 0 | return; |
580 | | |
581 | 0 | VariableSP var = var_list.GetVariableAtIndex(0); |
582 | 0 | decl = var->GetDeclaration(); |
583 | 0 | } |
584 | | |
585 | | addr_t InstrumentationRuntimeTSan::GetFirstNonInternalFramePc( |
586 | 0 | StructuredData::ObjectSP trace, bool skip_one_frame) { |
587 | 0 | ProcessSP process_sp = GetProcessSP(); |
588 | 0 | ModuleSP runtime_module_sp = GetRuntimeModuleSP(); |
589 | |
|
590 | 0 | StructuredData::Array *trace_array = trace->GetAsArray(); |
591 | 0 | for (size_t i = 0; i < trace_array->GetSize(); i++) { |
592 | 0 | if (skip_one_frame && i == 0) |
593 | 0 | continue; |
594 | | |
595 | 0 | addr_t addr; |
596 | 0 | if (!trace_array->GetItemAtIndexAsInteger(i, addr)) |
597 | 0 | continue; |
598 | | |
599 | 0 | lldb_private::Address so_addr; |
600 | 0 | if (!process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress( |
601 | 0 | addr, so_addr)) |
602 | 0 | continue; |
603 | | |
604 | 0 | if (so_addr.GetModule() == runtime_module_sp) |
605 | 0 | continue; |
606 | | |
607 | 0 | return addr; |
608 | 0 | } |
609 | | |
610 | 0 | return 0; |
611 | 0 | } |
612 | | |
613 | | std::string |
614 | 0 | InstrumentationRuntimeTSan::GenerateSummary(StructuredData::ObjectSP report) { |
615 | 0 | ProcessSP process_sp = GetProcessSP(); |
616 | |
|
617 | 0 | std::string summary = std::string(report->GetAsDictionary() |
618 | 0 | ->GetValueForKey("description") |
619 | 0 | ->GetAsString() |
620 | 0 | ->GetValue()); |
621 | 0 | bool skip_one_frame = |
622 | 0 | report->GetObjectForDotSeparatedPath("issue_type")->GetStringValue() == |
623 | 0 | "external-race"; |
624 | |
|
625 | 0 | addr_t pc = 0; |
626 | 0 | if (report->GetAsDictionary() |
627 | 0 | ->GetValueForKey("mops") |
628 | 0 | ->GetAsArray() |
629 | 0 | ->GetSize() > 0) |
630 | 0 | pc = GetFirstNonInternalFramePc(report->GetAsDictionary() |
631 | 0 | ->GetValueForKey("mops") |
632 | 0 | ->GetAsArray() |
633 | 0 | ->GetItemAtIndex(0) |
634 | 0 | ->GetAsDictionary() |
635 | 0 | ->GetValueForKey("trace"), |
636 | 0 | skip_one_frame); |
637 | |
|
638 | 0 | if (report->GetAsDictionary() |
639 | 0 | ->GetValueForKey("stacks") |
640 | 0 | ->GetAsArray() |
641 | 0 | ->GetSize() > 0) |
642 | 0 | pc = GetFirstNonInternalFramePc(report->GetAsDictionary() |
643 | 0 | ->GetValueForKey("stacks") |
644 | 0 | ->GetAsArray() |
645 | 0 | ->GetItemAtIndex(0) |
646 | 0 | ->GetAsDictionary() |
647 | 0 | ->GetValueForKey("trace"), |
648 | 0 | skip_one_frame); |
649 | |
|
650 | 0 | if (pc != 0) { |
651 | 0 | summary = summary + " in " + GetSymbolNameFromAddress(process_sp, pc); |
652 | 0 | } |
653 | |
|
654 | 0 | if (report->GetAsDictionary() |
655 | 0 | ->GetValueForKey("locs") |
656 | 0 | ->GetAsArray() |
657 | 0 | ->GetSize() > 0) { |
658 | 0 | StructuredData::ObjectSP loc = report->GetAsDictionary() |
659 | 0 | ->GetValueForKey("locs") |
660 | 0 | ->GetAsArray() |
661 | 0 | ->GetItemAtIndex(0); |
662 | 0 | std::string object_type = std::string(loc->GetAsDictionary() |
663 | 0 | ->GetValueForKey("object_type") |
664 | 0 | ->GetAsString() |
665 | 0 | ->GetValue()); |
666 | 0 | if (!object_type.empty()) { |
667 | 0 | summary = "Race on " + object_type + " object"; |
668 | 0 | } |
669 | 0 | addr_t addr = loc->GetAsDictionary() |
670 | 0 | ->GetValueForKey("address") |
671 | 0 | ->GetUnsignedIntegerValue(); |
672 | 0 | if (addr == 0) |
673 | 0 | addr = loc->GetAsDictionary() |
674 | 0 | ->GetValueForKey("start") |
675 | 0 | ->GetUnsignedIntegerValue(); |
676 | |
|
677 | 0 | if (addr != 0) { |
678 | 0 | std::string global_name = GetSymbolNameFromAddress(process_sp, addr); |
679 | 0 | if (!global_name.empty()) { |
680 | 0 | summary = summary + " at " + global_name; |
681 | 0 | } else { |
682 | 0 | summary = summary + " at " + Sprintf("0x%llx", addr); |
683 | 0 | } |
684 | 0 | } else { |
685 | 0 | int fd = loc->GetAsDictionary() |
686 | 0 | ->GetValueForKey("file_descriptor") |
687 | 0 | ->GetSignedIntegerValue(); |
688 | 0 | if (fd != 0) { |
689 | 0 | summary = summary + " on file descriptor " + Sprintf("%d", fd); |
690 | 0 | } |
691 | 0 | } |
692 | 0 | } |
693 | |
|
694 | 0 | return summary; |
695 | 0 | } |
696 | | |
697 | | addr_t InstrumentationRuntimeTSan::GetMainRacyAddress( |
698 | 0 | StructuredData::ObjectSP report) { |
699 | 0 | addr_t result = (addr_t)-1; |
700 | |
|
701 | 0 | report->GetObjectForDotSeparatedPath("mops")->GetAsArray()->ForEach( |
702 | 0 | [&result](StructuredData::Object *o) -> bool { |
703 | 0 | addr_t addr = o->GetObjectForDotSeparatedPath("address") |
704 | 0 | ->GetUnsignedIntegerValue(); |
705 | 0 | if (addr < result) |
706 | 0 | result = addr; |
707 | 0 | return true; |
708 | 0 | }); |
709 | |
|
710 | 0 | return (result == (addr_t)-1) ? 0 : result; |
711 | 0 | } |
712 | | |
713 | | std::string InstrumentationRuntimeTSan::GetLocationDescription( |
714 | | StructuredData::ObjectSP report, addr_t &global_addr, |
715 | 0 | std::string &global_name, std::string &filename, uint32_t &line) { |
716 | 0 | std::string result; |
717 | |
|
718 | 0 | ProcessSP process_sp = GetProcessSP(); |
719 | |
|
720 | 0 | if (report->GetAsDictionary() |
721 | 0 | ->GetValueForKey("locs") |
722 | 0 | ->GetAsArray() |
723 | 0 | ->GetSize() > 0) { |
724 | 0 | StructuredData::ObjectSP loc = report->GetAsDictionary() |
725 | 0 | ->GetValueForKey("locs") |
726 | 0 | ->GetAsArray() |
727 | 0 | ->GetItemAtIndex(0); |
728 | 0 | std::string type = std::string( |
729 | 0 | loc->GetAsDictionary()->GetValueForKey("type")->GetStringValue()); |
730 | 0 | if (type == "global") { |
731 | 0 | global_addr = loc->GetAsDictionary() |
732 | 0 | ->GetValueForKey("address") |
733 | 0 | ->GetUnsignedIntegerValue(); |
734 | |
|
735 | 0 | global_name = GetSymbolNameFromAddress(process_sp, global_addr); |
736 | 0 | if (!global_name.empty()) { |
737 | 0 | result = Sprintf("'%s' is a global variable (0x%llx)", |
738 | 0 | global_name.c_str(), global_addr); |
739 | 0 | } else { |
740 | 0 | result = Sprintf("0x%llx is a global variable", global_addr); |
741 | 0 | } |
742 | |
|
743 | 0 | Declaration decl; |
744 | 0 | GetSymbolDeclarationFromAddress(process_sp, global_addr, decl); |
745 | 0 | if (decl.GetFile()) { |
746 | 0 | filename = decl.GetFile().GetPath(); |
747 | 0 | line = decl.GetLine(); |
748 | 0 | } |
749 | 0 | } else if (type == "heap") { |
750 | 0 | addr_t addr = loc->GetAsDictionary() |
751 | 0 | ->GetValueForKey("start") |
752 | 0 | ->GetUnsignedIntegerValue(); |
753 | |
|
754 | 0 | size_t size = loc->GetAsDictionary() |
755 | 0 | ->GetValueForKey("size") |
756 | 0 | ->GetUnsignedIntegerValue(); |
757 | |
|
758 | 0 | std::string object_type = std::string(loc->GetAsDictionary() |
759 | 0 | ->GetValueForKey("object_type") |
760 | 0 | ->GetAsString() |
761 | 0 | ->GetValue()); |
762 | 0 | if (!object_type.empty()) { |
763 | 0 | result = Sprintf("Location is a %ld-byte %s object at 0x%llx", size, |
764 | 0 | object_type.c_str(), addr); |
765 | 0 | } else { |
766 | 0 | result = |
767 | 0 | Sprintf("Location is a %ld-byte heap object at 0x%llx", size, addr); |
768 | 0 | } |
769 | 0 | } else if (type == "stack") { |
770 | 0 | tid_t tid = loc->GetAsDictionary() |
771 | 0 | ->GetValueForKey("thread_id") |
772 | 0 | ->GetUnsignedIntegerValue(); |
773 | |
|
774 | 0 | result = Sprintf("Location is stack of thread %d", tid); |
775 | 0 | } else if (type == "tls") { |
776 | 0 | tid_t tid = loc->GetAsDictionary() |
777 | 0 | ->GetValueForKey("thread_id") |
778 | 0 | ->GetUnsignedIntegerValue(); |
779 | |
|
780 | 0 | result = Sprintf("Location is TLS of thread %d", tid); |
781 | 0 | } else if (type == "fd") { |
782 | 0 | int fd = loc->GetAsDictionary() |
783 | 0 | ->GetValueForKey("file_descriptor") |
784 | 0 | ->GetSignedIntegerValue(); |
785 | |
|
786 | 0 | result = Sprintf("Location is file descriptor %d", fd); |
787 | 0 | } |
788 | 0 | } |
789 | |
|
790 | 0 | return result; |
791 | 0 | } |
792 | | |
793 | | bool InstrumentationRuntimeTSan::NotifyBreakpointHit( |
794 | | void *baton, StoppointCallbackContext *context, user_id_t break_id, |
795 | 0 | user_id_t break_loc_id) { |
796 | 0 | assert(baton && "null baton"); |
797 | 0 | if (!baton) |
798 | 0 | return false; |
799 | | |
800 | 0 | InstrumentationRuntimeTSan *const instance = |
801 | 0 | static_cast<InstrumentationRuntimeTSan *>(baton); |
802 | |
|
803 | 0 | ProcessSP process_sp = instance->GetProcessSP(); |
804 | |
|
805 | 0 | if (process_sp->GetModIDRef().IsLastResumeForUserExpression()) |
806 | 0 | return false; |
807 | | |
808 | 0 | StructuredData::ObjectSP report = |
809 | 0 | instance->RetrieveReportData(context->exe_ctx_ref); |
810 | 0 | std::string stop_reason_description = |
811 | 0 | "unknown thread sanitizer fault (unable to extract thread sanitizer " |
812 | 0 | "report)"; |
813 | 0 | if (report) { |
814 | 0 | std::string issue_description = instance->FormatDescription(report); |
815 | 0 | report->GetAsDictionary()->AddStringItem("description", issue_description); |
816 | 0 | stop_reason_description = issue_description + " detected"; |
817 | 0 | report->GetAsDictionary()->AddStringItem("stop_description", |
818 | 0 | stop_reason_description); |
819 | 0 | std::string summary = instance->GenerateSummary(report); |
820 | 0 | report->GetAsDictionary()->AddStringItem("summary", summary); |
821 | 0 | addr_t main_address = instance->GetMainRacyAddress(report); |
822 | 0 | report->GetAsDictionary()->AddIntegerItem("memory_address", main_address); |
823 | |
|
824 | 0 | addr_t global_addr = 0; |
825 | 0 | std::string global_name; |
826 | 0 | std::string location_filename; |
827 | 0 | uint32_t location_line = 0; |
828 | 0 | std::string location_description = instance->GetLocationDescription( |
829 | 0 | report, global_addr, global_name, location_filename, location_line); |
830 | 0 | report->GetAsDictionary()->AddStringItem("location_description", |
831 | 0 | location_description); |
832 | 0 | if (global_addr != 0) { |
833 | 0 | report->GetAsDictionary()->AddIntegerItem("global_address", global_addr); |
834 | 0 | } |
835 | 0 | if (!global_name.empty()) { |
836 | 0 | report->GetAsDictionary()->AddStringItem("global_name", global_name); |
837 | 0 | } |
838 | 0 | if (location_filename != "") { |
839 | 0 | report->GetAsDictionary()->AddStringItem("location_filename", |
840 | 0 | location_filename); |
841 | 0 | report->GetAsDictionary()->AddIntegerItem("location_line", location_line); |
842 | 0 | } |
843 | |
|
844 | 0 | bool all_addresses_are_same = true; |
845 | 0 | report->GetObjectForDotSeparatedPath("mops")->GetAsArray()->ForEach( |
846 | 0 | [&all_addresses_are_same, |
847 | 0 | main_address](StructuredData::Object *o) -> bool { |
848 | 0 | addr_t addr = o->GetObjectForDotSeparatedPath("address") |
849 | 0 | ->GetUnsignedIntegerValue(); |
850 | 0 | if (main_address != addr) |
851 | 0 | all_addresses_are_same = false; |
852 | 0 | return true; |
853 | 0 | }); |
854 | 0 | report->GetAsDictionary()->AddBooleanItem("all_addresses_are_same", |
855 | 0 | all_addresses_are_same); |
856 | 0 | } |
857 | | |
858 | | // Make sure this is the right process |
859 | 0 | if (process_sp && process_sp == context->exe_ctx_ref.GetProcessSP()) { |
860 | 0 | ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP(); |
861 | 0 | if (thread_sp) |
862 | 0 | thread_sp->SetStopInfo( |
863 | 0 | InstrumentationRuntimeStopInfo:: |
864 | 0 | CreateStopReasonWithInstrumentationData( |
865 | 0 | *thread_sp, stop_reason_description, report)); |
866 | |
|
867 | 0 | StreamFile &s = process_sp->GetTarget().GetDebugger().GetOutputStream(); |
868 | 0 | s.Printf("ThreadSanitizer report breakpoint hit. Use 'thread " |
869 | 0 | "info -s' to get extended information about the " |
870 | 0 | "report.\n"); |
871 | |
|
872 | 0 | return true; // Return true to stop the target |
873 | 0 | } else |
874 | 0 | return false; // Let target run |
875 | 0 | } |
876 | | |
877 | | const RegularExpression & |
878 | 115k | InstrumentationRuntimeTSan::GetPatternForRuntimeLibrary() { |
879 | 115k | static RegularExpression regex(llvm::StringRef("libclang_rt.tsan_")); |
880 | 115k | return regex; |
881 | 115k | } |
882 | | |
883 | | bool InstrumentationRuntimeTSan::CheckIfRuntimeIsValid( |
884 | 2.24k | const lldb::ModuleSP module_sp) { |
885 | 2.24k | static ConstString g_tsan_get_current_report("__tsan_get_current_report"); |
886 | 2.24k | const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType( |
887 | 2.24k | g_tsan_get_current_report, lldb::eSymbolTypeAny); |
888 | 2.24k | return symbol != nullptr; |
889 | 2.24k | } |
890 | | |
891 | 0 | void InstrumentationRuntimeTSan::Activate() { |
892 | 0 | if (IsActive()) |
893 | 0 | return; |
894 | | |
895 | 0 | ProcessSP process_sp = GetProcessSP(); |
896 | 0 | if (!process_sp) |
897 | 0 | return; |
898 | | |
899 | 0 | ConstString symbol_name("__tsan_on_report"); |
900 | 0 | const Symbol *symbol = GetRuntimeModuleSP()->FindFirstSymbolWithNameAndType( |
901 | 0 | symbol_name, eSymbolTypeCode); |
902 | |
|
903 | 0 | if (symbol == nullptr) |
904 | 0 | return; |
905 | | |
906 | 0 | if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid()) |
907 | 0 | return; |
908 | | |
909 | 0 | Target &target = process_sp->GetTarget(); |
910 | 0 | addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target); |
911 | |
|
912 | 0 | if (symbol_address == LLDB_INVALID_ADDRESS) |
913 | 0 | return; |
914 | | |
915 | 0 | const bool internal = true; |
916 | 0 | const bool hardware = false; |
917 | 0 | const bool sync = false; |
918 | 0 | Breakpoint *breakpoint = |
919 | 0 | process_sp->GetTarget() |
920 | 0 | .CreateBreakpoint(symbol_address, internal, hardware) |
921 | 0 | .get(); |
922 | 0 | breakpoint->SetCallback(InstrumentationRuntimeTSan::NotifyBreakpointHit, this, |
923 | 0 | sync); |
924 | 0 | breakpoint->SetBreakpointKind("thread-sanitizer-report"); |
925 | 0 | SetBreakpointID(breakpoint->GetID()); |
926 | |
|
927 | 0 | SetActive(true); |
928 | 0 | } |
929 | | |
930 | 2.25k | void InstrumentationRuntimeTSan::Deactivate() { |
931 | 2.25k | if (GetBreakpointID() != LLDB_INVALID_BREAK_ID) { |
932 | 0 | ProcessSP process_sp = GetProcessSP(); |
933 | 0 | if (process_sp) { |
934 | 0 | process_sp->GetTarget().RemoveBreakpointByID(GetBreakpointID()); |
935 | 0 | SetBreakpointID(LLDB_INVALID_BREAK_ID); |
936 | 0 | } |
937 | 0 | } |
938 | 2.25k | SetActive(false); |
939 | 2.25k | } |
940 | | static std::string GenerateThreadName(const std::string &path, |
941 | | StructuredData::Object *o, |
942 | 0 | StructuredData::ObjectSP main_info) { |
943 | 0 | std::string result = "additional information"; |
944 | |
|
945 | 0 | if (path == "mops") { |
946 | 0 | size_t size = |
947 | 0 | o->GetObjectForDotSeparatedPath("size")->GetUnsignedIntegerValue(); |
948 | 0 | tid_t thread_id = |
949 | 0 | o->GetObjectForDotSeparatedPath("thread_id")->GetUnsignedIntegerValue(); |
950 | 0 | bool is_write = |
951 | 0 | o->GetObjectForDotSeparatedPath("is_write")->GetBooleanValue(); |
952 | 0 | bool is_atomic = |
953 | 0 | o->GetObjectForDotSeparatedPath("is_atomic")->GetBooleanValue(); |
954 | 0 | addr_t addr = |
955 | 0 | o->GetObjectForDotSeparatedPath("address")->GetUnsignedIntegerValue(); |
956 | |
|
957 | 0 | std::string addr_string = Sprintf(" at 0x%llx", addr); |
958 | |
|
959 | 0 | if (main_info->GetObjectForDotSeparatedPath("all_addresses_are_same") |
960 | 0 | ->GetBooleanValue()) { |
961 | 0 | addr_string = ""; |
962 | 0 | } |
963 | |
|
964 | 0 | if (main_info->GetObjectForDotSeparatedPath("issue_type") |
965 | 0 | ->GetStringValue() == "external-race") { |
966 | 0 | result = Sprintf("%s access by thread %d", |
967 | 0 | is_write ? "mutating" : "read-only", thread_id); |
968 | 0 | } else if (main_info->GetObjectForDotSeparatedPath("issue_type") |
969 | 0 | ->GetStringValue() == "swift-access-race") { |
970 | 0 | result = Sprintf("modifying access by thread %d", thread_id); |
971 | 0 | } else { |
972 | 0 | result = Sprintf("%s%s of size %zu%s by thread %" PRIu64, |
973 | 0 | is_atomic ? "atomic " : "", is_write ? "write" : "read", |
974 | 0 | size, addr_string.c_str(), thread_id); |
975 | 0 | } |
976 | 0 | } |
977 | |
|
978 | 0 | if (path == "threads") { |
979 | 0 | tid_t thread_id = |
980 | 0 | o->GetObjectForDotSeparatedPath("thread_id")->GetUnsignedIntegerValue(); |
981 | 0 | result = Sprintf("Thread %zu created", thread_id); |
982 | 0 | } |
983 | |
|
984 | 0 | if (path == "locs") { |
985 | 0 | std::string type = std::string( |
986 | 0 | o->GetAsDictionary()->GetValueForKey("type")->GetStringValue()); |
987 | 0 | tid_t thread_id = |
988 | 0 | o->GetObjectForDotSeparatedPath("thread_id")->GetUnsignedIntegerValue(); |
989 | 0 | int fd = o->GetObjectForDotSeparatedPath("file_descriptor") |
990 | 0 | ->GetSignedIntegerValue(); |
991 | 0 | if (type == "heap") { |
992 | 0 | result = Sprintf("Heap block allocated by thread %" PRIu64, thread_id); |
993 | 0 | } else if (type == "fd") { |
994 | 0 | result = Sprintf("File descriptor %d created by thread %" PRIu64, fd, |
995 | 0 | thread_id); |
996 | 0 | } |
997 | 0 | } |
998 | |
|
999 | 0 | if (path == "mutexes") { |
1000 | 0 | int mutex_id = |
1001 | 0 | o->GetObjectForDotSeparatedPath("mutex_id")->GetSignedIntegerValue(); |
1002 | |
|
1003 | 0 | result = Sprintf("Mutex M%d created", mutex_id); |
1004 | 0 | } |
1005 | |
|
1006 | 0 | if (path == "stacks") { |
1007 | 0 | tid_t thread_id = |
1008 | 0 | o->GetObjectForDotSeparatedPath("thread_id")->GetUnsignedIntegerValue(); |
1009 | 0 | result = Sprintf("Thread %" PRIu64, thread_id); |
1010 | 0 | } |
1011 | |
|
1012 | 0 | result[0] = toupper(result[0]); |
1013 | |
|
1014 | 0 | return result; |
1015 | 0 | } |
1016 | | |
1017 | | static void AddThreadsForPath(const std::string &path, |
1018 | | ThreadCollectionSP threads, ProcessSP process_sp, |
1019 | 0 | StructuredData::ObjectSP info) { |
1020 | 0 | info->GetObjectForDotSeparatedPath(path)->GetAsArray()->ForEach( |
1021 | 0 | [process_sp, threads, path, info](StructuredData::Object *o) -> bool { |
1022 | 0 | std::vector<lldb::addr_t> pcs; |
1023 | 0 | o->GetObjectForDotSeparatedPath("trace")->GetAsArray()->ForEach( |
1024 | 0 | [&pcs](StructuredData::Object *pc) -> bool { |
1025 | 0 | pcs.push_back(pc->GetUnsignedIntegerValue()); |
1026 | 0 | return true; |
1027 | 0 | }); |
1028 | |
|
1029 | 0 | if (pcs.size() == 0) |
1030 | 0 | return true; |
1031 | | |
1032 | 0 | StructuredData::ObjectSP thread_id_obj = |
1033 | 0 | o->GetObjectForDotSeparatedPath("thread_os_id"); |
1034 | 0 | tid_t tid = |
1035 | 0 | thread_id_obj ? thread_id_obj->GetUnsignedIntegerValue() : 0; |
1036 | |
|
1037 | 0 | ThreadSP new_thread_sp = |
1038 | 0 | std::make_shared<HistoryThread>(*process_sp, tid, pcs); |
1039 | 0 | new_thread_sp->SetName(GenerateThreadName(path, o, info).c_str()); |
1040 | | |
1041 | | // Save this in the Process' ExtendedThreadList so a strong pointer |
1042 | | // retains the object |
1043 | 0 | process_sp->GetExtendedThreadList().AddThread(new_thread_sp); |
1044 | 0 | threads->AddThread(new_thread_sp); |
1045 | |
|
1046 | 0 | return true; |
1047 | 0 | }); |
1048 | 0 | } |
1049 | | |
1050 | | lldb::ThreadCollectionSP |
1051 | | InstrumentationRuntimeTSan::GetBacktracesFromExtendedStopInfo( |
1052 | 0 | StructuredData::ObjectSP info) { |
1053 | |
|
1054 | 0 | ThreadCollectionSP threads = std::make_shared<ThreadCollection>(); |
1055 | |
|
1056 | 0 | if (info->GetObjectForDotSeparatedPath("instrumentation_class") |
1057 | 0 | ->GetStringValue() != "ThreadSanitizer") |
1058 | 0 | return threads; |
1059 | | |
1060 | 0 | ProcessSP process_sp = GetProcessSP(); |
1061 | |
|
1062 | 0 | AddThreadsForPath("stacks", threads, process_sp, info); |
1063 | 0 | AddThreadsForPath("mops", threads, process_sp, info); |
1064 | 0 | AddThreadsForPath("locs", threads, process_sp, info); |
1065 | 0 | AddThreadsForPath("mutexes", threads, process_sp, info); |
1066 | 0 | AddThreadsForPath("threads", threads, process_sp, info); |
1067 | |
|
1068 | 0 | return threads; |
1069 | 0 | } |