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