1320384Sdim//===-- MainThreadCheckerRuntime.cpp ----------------------------*- C++ -*-===// 2320384Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6320384Sdim// 7320384Sdim//===----------------------------------------------------------------------===// 8320384Sdim 9320384Sdim#include "MainThreadCheckerRuntime.h" 10320384Sdim 11320384Sdim#include "lldb/Breakpoint/StoppointCallbackContext.h" 12320384Sdim#include "lldb/Core/Module.h" 13320384Sdim#include "lldb/Core/PluginManager.h" 14320384Sdim#include "lldb/Symbol/Symbol.h" 15320384Sdim#include "lldb/Symbol/SymbolContext.h" 16320384Sdim#include "lldb/Symbol/Variable.h" 17320384Sdim#include "lldb/Symbol/VariableList.h" 18320384Sdim#include "lldb/Target/InstrumentationRuntimeStopInfo.h" 19320384Sdim#include "lldb/Target/RegisterContext.h" 20320384Sdim#include "lldb/Target/SectionLoadList.h" 21320384Sdim#include "lldb/Target/StopInfo.h" 22320384Sdim#include "lldb/Target/Target.h" 23320384Sdim#include "lldb/Target/Thread.h" 24320384Sdim#include "lldb/Utility/RegularExpression.h" 25320384Sdim#include "Plugins/Process/Utility/HistoryThread.h" 26320384Sdim 27353358Sdim#include <memory> 28353358Sdim 29320384Sdimusing namespace lldb; 30320384Sdimusing namespace lldb_private; 31320384Sdim 32320384SdimMainThreadCheckerRuntime::~MainThreadCheckerRuntime() { 33320384Sdim Deactivate(); 34320384Sdim} 35320384Sdim 36320384Sdimlldb::InstrumentationRuntimeSP 37320384SdimMainThreadCheckerRuntime::CreateInstance(const lldb::ProcessSP &process_sp) { 38320384Sdim return InstrumentationRuntimeSP(new MainThreadCheckerRuntime(process_sp)); 39320384Sdim} 40320384Sdim 41320384Sdimvoid MainThreadCheckerRuntime::Initialize() { 42320384Sdim PluginManager::RegisterPlugin( 43320384Sdim GetPluginNameStatic(), "MainThreadChecker instrumentation runtime plugin.", 44320384Sdim CreateInstance, GetTypeStatic); 45320384Sdim} 46320384Sdim 47320384Sdimvoid MainThreadCheckerRuntime::Terminate() { 48320384Sdim PluginManager::UnregisterPlugin(CreateInstance); 49320384Sdim} 50320384Sdim 51320384Sdimlldb_private::ConstString MainThreadCheckerRuntime::GetPluginNameStatic() { 52320384Sdim return ConstString("MainThreadChecker"); 53320384Sdim} 54320384Sdim 55320384Sdimlldb::InstrumentationRuntimeType MainThreadCheckerRuntime::GetTypeStatic() { 56320384Sdim return eInstrumentationRuntimeTypeMainThreadChecker; 57320384Sdim} 58320384Sdim 59320384Sdimconst RegularExpression & 60320384SdimMainThreadCheckerRuntime::GetPatternForRuntimeLibrary() { 61320384Sdim static RegularExpression regex(llvm::StringRef("libMainThreadChecker.dylib")); 62320384Sdim return regex; 63320384Sdim} 64320384Sdim 65320384Sdimbool MainThreadCheckerRuntime::CheckIfRuntimeIsValid( 66320384Sdim const lldb::ModuleSP module_sp) { 67320384Sdim static ConstString test_sym("__main_thread_checker_on_report"); 68320384Sdim const Symbol *symbol = 69320384Sdim module_sp->FindFirstSymbolWithNameAndType(test_sym, lldb::eSymbolTypeAny); 70320384Sdim return symbol != nullptr; 71320384Sdim} 72320384Sdim 73320384SdimStructuredData::ObjectSP 74320384SdimMainThreadCheckerRuntime::RetrieveReportData(ExecutionContextRef exe_ctx_ref) { 75320384Sdim ProcessSP process_sp = GetProcessSP(); 76320384Sdim if (!process_sp) 77320384Sdim return StructuredData::ObjectSP(); 78320384Sdim 79320384Sdim ThreadSP thread_sp = exe_ctx_ref.GetThreadSP(); 80320384Sdim StackFrameSP frame_sp = thread_sp->GetSelectedFrame(); 81320384Sdim ModuleSP runtime_module_sp = GetRuntimeModuleSP(); 82320384Sdim Target &target = process_sp->GetTarget(); 83320384Sdim 84320384Sdim if (!frame_sp) 85320384Sdim return StructuredData::ObjectSP(); 86320384Sdim 87320384Sdim RegisterContextSP regctx_sp = frame_sp->GetRegisterContext(); 88320384Sdim if (!regctx_sp) 89320384Sdim return StructuredData::ObjectSP(); 90320384Sdim 91320384Sdim const RegisterInfo *reginfo = regctx_sp->GetRegisterInfoByName("arg1"); 92320384Sdim if (!reginfo) 93320384Sdim return StructuredData::ObjectSP(); 94320384Sdim 95320384Sdim uint64_t apiname_ptr = regctx_sp->ReadRegisterAsUnsigned(reginfo, 0); 96320384Sdim if (!apiname_ptr) 97320384Sdim return StructuredData::ObjectSP(); 98320384Sdim 99320384Sdim std::string apiName = ""; 100320384Sdim Status read_error; 101320384Sdim target.ReadCStringFromMemory(apiname_ptr, apiName, read_error); 102320384Sdim if (read_error.Fail()) 103320384Sdim return StructuredData::ObjectSP(); 104320384Sdim 105320384Sdim std::string className = ""; 106320384Sdim std::string selector = ""; 107320384Sdim if (apiName.substr(0, 2) == "-[") { 108320384Sdim size_t spacePos = apiName.find(" "); 109320384Sdim if (spacePos != std::string::npos) { 110320384Sdim className = apiName.substr(2, spacePos - 2); 111320384Sdim selector = apiName.substr(spacePos + 1, apiName.length() - spacePos - 2); 112320384Sdim } 113320384Sdim } 114320384Sdim 115320384Sdim // Gather the PCs of the user frames in the backtrace. 116320384Sdim StructuredData::Array *trace = new StructuredData::Array(); 117320384Sdim auto trace_sp = StructuredData::ObjectSP(trace); 118320384Sdim StackFrameSP responsible_frame; 119320384Sdim for (unsigned I = 0; I < thread_sp->GetStackFrameCount(); ++I) { 120320384Sdim StackFrameSP frame = thread_sp->GetStackFrameAtIndex(I); 121320384Sdim Address addr = frame->GetFrameCodeAddress(); 122320384Sdim if (addr.GetModule() == runtime_module_sp) // Skip PCs from the runtime. 123320384Sdim continue; 124320384Sdim 125320384Sdim // The first non-runtime frame is responsible for the bug. 126320384Sdim if (!responsible_frame) 127320384Sdim responsible_frame = frame; 128320384Sdim 129320384Sdim // First frame in stacktrace should point to a real PC, not return address. 130320384Sdim if (I != 0 && trace->GetSize() == 0) { 131320384Sdim addr.Slide(-1); 132320384Sdim } 133320384Sdim 134320384Sdim lldb::addr_t PC = addr.GetLoadAddress(&target); 135320384Sdim trace->AddItem(StructuredData::ObjectSP(new StructuredData::Integer(PC))); 136320384Sdim } 137320384Sdim 138320384Sdim auto *d = new StructuredData::Dictionary(); 139320384Sdim auto dict_sp = StructuredData::ObjectSP(d); 140320384Sdim d->AddStringItem("instrumentation_class", "MainThreadChecker"); 141320384Sdim d->AddStringItem("api_name", apiName); 142320384Sdim d->AddStringItem("class_name", className); 143320384Sdim d->AddStringItem("selector", selector); 144320384Sdim d->AddStringItem("description", 145320970Sdim apiName + " must be used from main thread only"); 146320384Sdim d->AddIntegerItem("tid", thread_sp->GetIndexID()); 147320384Sdim d->AddItem("trace", trace_sp); 148320384Sdim return dict_sp; 149320384Sdim} 150320384Sdim 151320384Sdimbool MainThreadCheckerRuntime::NotifyBreakpointHit( 152320384Sdim void *baton, StoppointCallbackContext *context, user_id_t break_id, 153320384Sdim user_id_t break_loc_id) { 154320384Sdim assert(baton && "null baton"); 155320384Sdim if (!baton) 156360784Sdim return false; ///< false => resume execution. 157320384Sdim 158320384Sdim MainThreadCheckerRuntime *const instance = 159320384Sdim static_cast<MainThreadCheckerRuntime *>(baton); 160320384Sdim 161320384Sdim ProcessSP process_sp = instance->GetProcessSP(); 162320384Sdim ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP(); 163320384Sdim if (!process_sp || !thread_sp || 164320384Sdim process_sp != context->exe_ctx_ref.GetProcessSP()) 165320384Sdim return false; 166320384Sdim 167320970Sdim if (process_sp->GetModIDRef().IsLastResumeForUserExpression()) 168320970Sdim return false; 169320970Sdim 170320384Sdim StructuredData::ObjectSP report = 171320384Sdim instance->RetrieveReportData(context->exe_ctx_ref); 172320384Sdim 173320384Sdim if (report) { 174320384Sdim std::string description = report->GetAsDictionary() 175320384Sdim ->GetValueForKey("description") 176320384Sdim ->GetAsString() 177320384Sdim ->GetValue(); 178320384Sdim thread_sp->SetStopInfo( 179320384Sdim InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData( 180320384Sdim *thread_sp, description, report)); 181320384Sdim return true; 182320384Sdim } 183320384Sdim 184320384Sdim return false; 185320384Sdim} 186320384Sdim 187320384Sdimvoid MainThreadCheckerRuntime::Activate() { 188320384Sdim if (IsActive()) 189320384Sdim return; 190320384Sdim 191320384Sdim ProcessSP process_sp = GetProcessSP(); 192320384Sdim if (!process_sp) 193320384Sdim return; 194320384Sdim 195320384Sdim ModuleSP runtime_module_sp = GetRuntimeModuleSP(); 196320384Sdim 197320384Sdim ConstString symbol_name("__main_thread_checker_on_report"); 198320384Sdim const Symbol *symbol = runtime_module_sp->FindFirstSymbolWithNameAndType( 199320384Sdim symbol_name, eSymbolTypeCode); 200320384Sdim 201320384Sdim if (symbol == nullptr) 202320384Sdim return; 203320384Sdim 204320384Sdim if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid()) 205320384Sdim return; 206320384Sdim 207320384Sdim Target &target = process_sp->GetTarget(); 208320384Sdim addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target); 209320384Sdim 210320384Sdim if (symbol_address == LLDB_INVALID_ADDRESS) 211320384Sdim return; 212320384Sdim 213320384Sdim Breakpoint *breakpoint = 214320384Sdim process_sp->GetTarget() 215320384Sdim .CreateBreakpoint(symbol_address, /*internal=*/true, 216320384Sdim /*hardware=*/false) 217320384Sdim .get(); 218320384Sdim breakpoint->SetCallback(MainThreadCheckerRuntime::NotifyBreakpointHit, this, 219320384Sdim true); 220320384Sdim breakpoint->SetBreakpointKind("main-thread-checker-report"); 221320384Sdim SetBreakpointID(breakpoint->GetID()); 222320384Sdim 223320384Sdim SetActive(true); 224320384Sdim} 225320384Sdim 226320384Sdimvoid MainThreadCheckerRuntime::Deactivate() { 227320384Sdim SetActive(false); 228320384Sdim 229320384Sdim auto BID = GetBreakpointID(); 230320384Sdim if (BID == LLDB_INVALID_BREAK_ID) 231320384Sdim return; 232320384Sdim 233320384Sdim if (ProcessSP process_sp = GetProcessSP()) { 234320384Sdim process_sp->GetTarget().RemoveBreakpointByID(BID); 235320384Sdim SetBreakpointID(LLDB_INVALID_BREAK_ID); 236320384Sdim } 237320384Sdim} 238320384Sdim 239320384Sdimlldb::ThreadCollectionSP 240320384SdimMainThreadCheckerRuntime::GetBacktracesFromExtendedStopInfo( 241320384Sdim StructuredData::ObjectSP info) { 242320384Sdim ThreadCollectionSP threads; 243353358Sdim threads = std::make_shared<ThreadCollection>(); 244360784Sdim 245320384Sdim ProcessSP process_sp = GetProcessSP(); 246360784Sdim 247320384Sdim if (info->GetObjectForDotSeparatedPath("instrumentation_class") 248320384Sdim ->GetStringValue() != "MainThreadChecker") 249320384Sdim return threads; 250360784Sdim 251320384Sdim std::vector<lldb::addr_t> PCs; 252320384Sdim auto trace = info->GetObjectForDotSeparatedPath("trace")->GetAsArray(); 253320384Sdim trace->ForEach([&PCs](StructuredData::Object *PC) -> bool { 254320384Sdim PCs.push_back(PC->GetAsInteger()->GetValue()); 255320384Sdim return true; 256320384Sdim }); 257360784Sdim 258320384Sdim if (PCs.empty()) 259320384Sdim return threads; 260360784Sdim 261320384Sdim StructuredData::ObjectSP thread_id_obj = 262320384Sdim info->GetObjectForDotSeparatedPath("tid"); 263320384Sdim tid_t tid = thread_id_obj ? thread_id_obj->GetIntegerValue() : 0; 264353358Sdim 265353358Sdim HistoryThread *history_thread = new HistoryThread(*process_sp, tid, PCs); 266320384Sdim ThreadSP new_thread_sp(history_thread); 267360784Sdim 268341825Sdim // Save this in the Process' ExtendedThreadList so a strong pointer retains 269341825Sdim // the object 270320384Sdim process_sp->GetExtendedThreadList().AddThread(new_thread_sp); 271320384Sdim threads->AddThread(new_thread_sp); 272320384Sdim 273320384Sdim return threads; 274320384Sdim} 275