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