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