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