brintos

brintos / llvm-project-archived public Read only

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