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