280 lines · cpp
1//===-- InstrumentationRuntimeMainThreadChecker.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 "InstrumentationRuntimeMainThreadChecker.h"10 11#include "Plugins/Process/Utility/HistoryThread.h"12#include "lldb/Breakpoint/StoppointCallbackContext.h"13#include "lldb/Core/Module.h"14#include "lldb/Core/PluginManager.h"15#include "lldb/Symbol/Symbol.h"16#include "lldb/Symbol/SymbolContext.h"17#include "lldb/Symbol/Variable.h"18#include "lldb/Symbol/VariableList.h"19#include "lldb/Target/InstrumentationRuntimeStopInfo.h"20#include "lldb/Target/RegisterContext.h"21#include "lldb/Target/SectionLoadList.h"22#include "lldb/Target/StopInfo.h"23#include "lldb/Target/Target.h"24#include "lldb/Target/Thread.h"25#include "lldb/Utility/RegularExpression.h"26 27#include <memory>28 29using namespace lldb;30using namespace lldb_private;31 32LLDB_PLUGIN_DEFINE(InstrumentationRuntimeMainThreadChecker)33 34InstrumentationRuntimeMainThreadChecker::35 ~InstrumentationRuntimeMainThreadChecker() {36 Deactivate();37}38 39lldb::InstrumentationRuntimeSP40InstrumentationRuntimeMainThreadChecker::CreateInstance(41 const lldb::ProcessSP &process_sp) {42 return InstrumentationRuntimeSP(43 new InstrumentationRuntimeMainThreadChecker(process_sp));44}45 46void InstrumentationRuntimeMainThreadChecker::Initialize() {47 PluginManager::RegisterPlugin(48 GetPluginNameStatic(),49 "MainThreadChecker instrumentation runtime plugin.", CreateInstance,50 GetTypeStatic);51}52 53void InstrumentationRuntimeMainThreadChecker::Terminate() {54 PluginManager::UnregisterPlugin(CreateInstance);55}56 57lldb::InstrumentationRuntimeType58InstrumentationRuntimeMainThreadChecker::GetTypeStatic() {59 return eInstrumentationRuntimeTypeMainThreadChecker;60}61 62const RegularExpression &63InstrumentationRuntimeMainThreadChecker::GetPatternForRuntimeLibrary() {64 static RegularExpression regex(llvm::StringRef("libMainThreadChecker.dylib"));65 return regex;66}67 68bool InstrumentationRuntimeMainThreadChecker::CheckIfRuntimeIsValid(69 const lldb::ModuleSP module_sp) {70 static ConstString test_sym("__main_thread_checker_on_report");71 const Symbol *symbol =72 module_sp->FindFirstSymbolWithNameAndType(test_sym, lldb::eSymbolTypeAny);73 return symbol != nullptr;74}75 76StructuredData::ObjectSP77InstrumentationRuntimeMainThreadChecker::RetrieveReportData(78 ExecutionContextRef exe_ctx_ref) {79 ProcessSP process_sp = GetProcessSP();80 if (!process_sp)81 return StructuredData::ObjectSP();82 83 ThreadSP thread_sp = exe_ctx_ref.GetThreadSP();84 StackFrameSP frame_sp =85 thread_sp->GetSelectedFrame(DoNoSelectMostRelevantFrame);86 ModuleSP runtime_module_sp = GetRuntimeModuleSP();87 Target &target = process_sp->GetTarget();88 89 if (!frame_sp)90 return StructuredData::ObjectSP();91 92 RegisterContextSP regctx_sp = frame_sp->GetRegisterContext();93 if (!regctx_sp)94 return StructuredData::ObjectSP();95 96 const RegisterInfo *reginfo = regctx_sp->GetRegisterInfoByName("arg1");97 if (!reginfo)98 return StructuredData::ObjectSP();99 100 uint64_t apiname_ptr = regctx_sp->ReadRegisterAsUnsigned(reginfo, 0);101 if (!apiname_ptr)102 return StructuredData::ObjectSP();103 104 std::string apiName;105 Status read_error;106 target.ReadCStringFromMemory(apiname_ptr, apiName, read_error);107 if (read_error.Fail())108 return StructuredData::ObjectSP();109 110 std::string className;111 std::string selector;112 if (apiName.substr(0, 2) == "-[") {113 size_t spacePos = apiName.find(' ');114 if (spacePos != std::string::npos) {115 className = apiName.substr(2, spacePos - 2);116 selector = apiName.substr(spacePos + 1, apiName.length() - spacePos - 2);117 }118 }119 120 // Gather the PCs of the user frames in the backtrace.121 StructuredData::Array *trace = new StructuredData::Array();122 auto trace_sp = StructuredData::ObjectSP(trace);123 StackFrameSP responsible_frame;124 for (unsigned I = 0; I < thread_sp->GetStackFrameCount(); ++I) {125 StackFrameSP frame = thread_sp->GetStackFrameAtIndex(I);126 Address addr = frame->GetFrameCodeAddressForSymbolication();127 if (addr.GetModule() == runtime_module_sp) // Skip PCs from the runtime.128 continue;129 130 // The first non-runtime frame is responsible for the bug.131 if (!responsible_frame)132 responsible_frame = frame;133 134 lldb::addr_t PC = addr.GetLoadAddress(&target);135 trace->AddIntegerItem(PC);136 }137 138 auto *d = new StructuredData::Dictionary();139 auto dict_sp = StructuredData::ObjectSP(d);140 d->AddStringItem("instrumentation_class", "MainThreadChecker");141 d->AddStringItem("api_name", apiName);142 d->AddStringItem("class_name", className);143 d->AddStringItem("selector", selector);144 d->AddStringItem("description",145 apiName + " must be used from main thread only");146 d->AddIntegerItem("tid", thread_sp->GetIndexID());147 d->AddItem("trace", trace_sp);148 return dict_sp;149}150 151bool InstrumentationRuntimeMainThreadChecker::NotifyBreakpointHit(152 void *baton, StoppointCallbackContext *context, user_id_t break_id,153 user_id_t break_loc_id) {154 assert(baton && "null baton");155 if (!baton)156 return false; ///< false => resume execution.157 158 InstrumentationRuntimeMainThreadChecker *const instance =159 static_cast<InstrumentationRuntimeMainThreadChecker *>(baton);160 161 ProcessSP process_sp = instance->GetProcessSP();162 ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP();163 if (!process_sp || !thread_sp ||164 process_sp != context->exe_ctx_ref.GetProcessSP())165 return false;166 167 if (process_sp->GetModIDRef().IsLastResumeForUserExpression())168 return false;169 170 StructuredData::ObjectSP report =171 instance->RetrieveReportData(context->exe_ctx_ref);172 173 if (report) {174 std::string description = std::string(report->GetAsDictionary()175 ->GetValueForKey("description")176 ->GetAsString()177 ->GetValue());178 thread_sp->SetStopInfo(179 InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData(180 *thread_sp, description, report));181 return true;182 }183 184 return false;185}186 187void InstrumentationRuntimeMainThreadChecker::Activate() {188 if (IsActive())189 return;190 191 ProcessSP process_sp = GetProcessSP();192 if (!process_sp)193 return;194 195 ModuleSP runtime_module_sp = GetRuntimeModuleSP();196 197 ConstString symbol_name("__main_thread_checker_on_report");198 const Symbol *symbol = runtime_module_sp->FindFirstSymbolWithNameAndType(199 symbol_name, eSymbolTypeCode);200 201 if (symbol == nullptr)202 return;203 204 if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid())205 return;206 207 Target &target = process_sp->GetTarget();208 addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target);209 210 if (symbol_address == LLDB_INVALID_ADDRESS)211 return;212 213 Breakpoint *breakpoint =214 process_sp->GetTarget()215 .CreateBreakpoint(symbol_address, /*internal=*/true,216 /*hardware=*/false)217 .get();218 const bool sync = false;219 breakpoint->SetCallback(220 InstrumentationRuntimeMainThreadChecker::NotifyBreakpointHit, this, sync);221 breakpoint->SetBreakpointKind("main-thread-checker-report");222 SetBreakpointID(breakpoint->GetID());223 224 SetActive(true);225}226 227void InstrumentationRuntimeMainThreadChecker::Deactivate() {228 SetActive(false);229 230 auto BID = GetBreakpointID();231 if (BID == LLDB_INVALID_BREAK_ID)232 return;233 234 if (ProcessSP process_sp = GetProcessSP()) {235 process_sp->GetTarget().RemoveBreakpointByID(BID);236 SetBreakpointID(LLDB_INVALID_BREAK_ID);237 }238}239 240lldb::ThreadCollectionSP241InstrumentationRuntimeMainThreadChecker::GetBacktracesFromExtendedStopInfo(242 StructuredData::ObjectSP info) {243 ThreadCollectionSP threads;244 threads = std::make_shared<ThreadCollection>();245 246 ProcessSP process_sp = GetProcessSP();247 248 if (info->GetObjectForDotSeparatedPath("instrumentation_class")249 ->GetStringValue() != "MainThreadChecker")250 return threads;251 252 std::vector<lldb::addr_t> PCs;253 auto trace = info->GetObjectForDotSeparatedPath("trace")->GetAsArray();254 trace->ForEach([&PCs](StructuredData::Object *PC) -> bool {255 PCs.push_back(PC->GetUnsignedIntegerValue());256 return true;257 });258 259 if (PCs.empty())260 return threads;261 262 StructuredData::ObjectSP thread_id_obj =263 info->GetObjectForDotSeparatedPath("tid");264 lldb::tid_t tid =265 thread_id_obj ? thread_id_obj->GetUnsignedIntegerValue() : 0;266 267 // We gather symbolication addresses above, so no need for HistoryThread to268 // try to infer the call addresses.269 auto pc_type = HistoryPCType::Calls;270 ThreadSP new_thread_sp =271 std::make_shared<HistoryThread>(*process_sp, tid, PCs, pc_type);272 273 // Save this in the Process' ExtendedThreadList so a strong pointer retains274 // the object275 process_sp->GetExtendedThreadList().AddThread(new_thread_sp);276 threads->AddThread(new_thread_sp);277 278 return threads;279}280