blob: 2709afc27433dbca7ff7a0506feeb9bc4b54fba6 [file] [log] [blame]
Ryan Brown65d4d5c2015-09-16 21:20:44 +00001//===-- OperatingSystemGo.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#include "OperatingSystemGo.h"
10
11// C Includes
12// C++ Includes
13#include <unordered_map>
14
15// Other libraries and framework includes
16#include "lldb/Core/DataBufferHeap.h"
17#include "lldb/Core/Debugger.h"
18#include "lldb/Core/Module.h"
19#include "lldb/Core/PluginManager.h"
20#include "lldb/Core/RegisterValue.h"
21#include "lldb/Core/Section.h"
22#include "lldb/Core/StreamString.h"
23#include "lldb/Core/ValueObjectVariable.h"
24#include "lldb/Interpreter/CommandInterpreter.h"
25#include "lldb/Interpreter/OptionValueProperties.h"
26#include "lldb/Interpreter/Options.h"
27#include "lldb/Interpreter/OptionGroupBoolean.h"
28#include "lldb/Interpreter/OptionGroupUInt64.h"
29#include "lldb/Interpreter/Property.h"
30#include "lldb/Symbol/ObjectFile.h"
31#include "lldb/Symbol/Type.h"
32#include "lldb/Symbol/VariableList.h"
33#include "lldb/Target/Process.h"
34#include "lldb/Target/StopInfo.h"
35#include "lldb/Target/Target.h"
36#include "lldb/Target/ThreadList.h"
37#include "lldb/Target/Thread.h"
38#include "Plugins/Process/Utility/DynamicRegisterInfo.h"
39#include "Plugins/Process/Utility/RegisterContextMemory.h"
40#include "Plugins/Process/Utility/ThreadMemory.h"
41
42using namespace lldb;
43using namespace lldb_private;
44
45namespace
46{
47
48static PropertyDefinition g_properties[] = {{"enable", OptionValue::eTypeBoolean, true, true, nullptr, nullptr,
49 "Specify whether goroutines should be treated as threads."},
50 {NULL, OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL}};
51
52enum
53{
54 ePropertyEnableGoroutines,
55};
56
57class PluginProperties : public Properties
58{
59 public:
60 static ConstString
61 GetSettingName()
62 {
63 return OperatingSystemGo::GetPluginNameStatic();
64 }
65
66 PluginProperties()
67 : Properties()
68 {
69 m_collection_sp.reset(new OptionValueProperties(GetSettingName()));
70 m_collection_sp->Initialize(g_properties);
71 }
72
73 virtual ~PluginProperties() {}
74
75 bool
76 GetEnableGoroutines()
77 {
78 const uint32_t idx = ePropertyEnableGoroutines;
79 return m_collection_sp->GetPropertyAtIndexAsBoolean(NULL, idx, g_properties[idx].default_uint_value);
80 }
81
82 bool
83 SetEnableGoroutines(bool enable)
84 {
85 const uint32_t idx = ePropertyEnableGoroutines;
86 return m_collection_sp->SetPropertyAtIndexAsUInt64(NULL, idx, enable);
87 }
88};
89
90typedef std::shared_ptr<PluginProperties> OperatingSystemGoPropertiesSP;
91
92static const OperatingSystemGoPropertiesSP &
93GetGlobalPluginProperties()
94{
95 static OperatingSystemGoPropertiesSP g_settings_sp;
96 if (!g_settings_sp)
97 g_settings_sp.reset(new PluginProperties());
98 return g_settings_sp;
99}
100
101class RegisterContextGo : public RegisterContextMemory
102{
103 public:
104 //------------------------------------------------------------------
105 // Constructors and Destructors
106 //------------------------------------------------------------------
107 RegisterContextGo(lldb_private::Thread &thread, uint32_t concrete_frame_idx, DynamicRegisterInfo &reg_info,
108 lldb::addr_t reg_data_addr)
109 : RegisterContextMemory(thread, concrete_frame_idx, reg_info, reg_data_addr)
110 {
111 const RegisterInfo *sp = reg_info.GetRegisterInfoAtIndex(
112 reg_info.ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP));
113 const RegisterInfo *pc = reg_info.GetRegisterInfoAtIndex(
114 reg_info.ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC));
115 size_t byte_size = std::max(sp->byte_offset + sp->byte_size, pc->byte_offset + pc->byte_size);
116
117 DataBufferSP reg_data_sp(new DataBufferHeap(byte_size, 0));
118 m_reg_data.SetData(reg_data_sp);
119 }
120
121 virtual ~RegisterContextGo() {}
122
123 virtual bool
124 ReadRegister(const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &reg_value)
125 {
126 switch (reg_info->kinds[eRegisterKindGeneric])
127 {
128 case LLDB_REGNUM_GENERIC_SP:
129 case LLDB_REGNUM_GENERIC_PC:
130 return RegisterContextMemory::ReadRegister(reg_info, reg_value);
131 default:
132 reg_value.SetValueToInvalid();
133 return true;
134 }
135 }
136
137 virtual bool
138 WriteRegister(const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &reg_value)
139 {
140 switch (reg_info->kinds[eRegisterKindGeneric])
141 {
142 case LLDB_REGNUM_GENERIC_SP:
143 case LLDB_REGNUM_GENERIC_PC:
144 return RegisterContextMemory::WriteRegister(reg_info, reg_value);
145 default:
146 return false;
147 }
148 }
149
150 private:
151 DISALLOW_COPY_AND_ASSIGN(RegisterContextGo);
152};
153
154} // namespace
155
156struct OperatingSystemGo::Goroutine
157{
158 uint64_t m_lostack;
159 uint64_t m_histack;
160 uint64_t m_goid;
161 addr_t m_gobuf;
162 uint32_t m_status;
163};
164
165void
166OperatingSystemGo::Initialize()
167{
168 PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance,
169 DebuggerInitialize);
170}
171
172void
173OperatingSystemGo::DebuggerInitialize(Debugger &debugger)
174{
175 if (!PluginManager::GetSettingForOperatingSystemPlugin(debugger, PluginProperties::GetSettingName()))
176 {
177 const bool is_global_setting = true;
178 PluginManager::CreateSettingForOperatingSystemPlugin(
179 debugger, GetGlobalPluginProperties()->GetValueProperties(),
180 ConstString("Properties for the goroutine thread plug-in."), is_global_setting);
181 }
182}
183
184void
185OperatingSystemGo::Terminate()
186{
187 PluginManager::UnregisterPlugin(CreateInstance);
188}
189
190OperatingSystem *
191OperatingSystemGo::CreateInstance(Process *process, bool force)
192{
193 if (!force)
194 {
195 TargetSP target_sp = process->CalculateTarget();
196 if (!target_sp)
197 return nullptr;
198 ModuleList &module_list = target_sp->GetImages();
199 Mutex::Locker modules_locker(module_list.GetMutex());
200 const size_t num_modules = module_list.GetSize();
201 bool found_go_runtime = false;
202 for (size_t i = 0; i < num_modules; ++i)
203 {
204 Module *module = module_list.GetModulePointerAtIndexUnlocked(i);
205 const SectionList *section_list = module->GetSectionList();
206 if (section_list)
207 {
208 SectionSP section_sp(section_list->FindSectionByType(eSectionTypeGoSymtab, true));
209 if (section_sp)
210 {
211 found_go_runtime = true;
212 break;
213 }
214 }
215 }
216 if (!found_go_runtime)
217 return nullptr;
218 }
219 return new OperatingSystemGo(process);
220}
221
222ConstString
223OperatingSystemGo::GetPluginNameStatic()
224{
225 static ConstString g_name("goroutines");
226 return g_name;
227}
228
229const char *
230OperatingSystemGo::GetPluginDescriptionStatic()
231{
232 return "Operating system plug-in that reads runtime data-structures for goroutines.";
233}
234
235OperatingSystemGo::OperatingSystemGo(lldb_private::Process *process)
236 : OperatingSystem(process)
237 , m_reginfo(new DynamicRegisterInfo)
238{
239}
240
241OperatingSystemGo::~OperatingSystemGo()
242{
243}
244
245bool
246OperatingSystemGo::Init(ThreadList &threads)
247{
248 if (threads.GetSize(false) < 1)
249 return false;
250 TargetSP target_sp = m_process->CalculateTarget();
251 if (!target_sp)
252 return false;
253 m_allg_sp = FindGlobal(target_sp, "runtime.allg");
254 m_allglen_sp = FindGlobal(target_sp, "runtime.allglen");
255
256 if (m_allg_sp && !m_allglen_sp)
257 {
258 StreamSP error_sp = target_sp->GetDebugger().GetAsyncErrorStream();
259 error_sp->Printf("Unsupported Go runtime version detected.");
260 return false;
261 }
262
263 if (!m_allg_sp)
264 return false;
265
266 RegisterContextSP real_registers_sp = threads.GetThreadAtIndex(0, false)->GetRegisterContext();
267
268 std::unordered_map<size_t, ConstString> register_sets;
269 for (size_t set_idx = 0; set_idx < real_registers_sp->GetRegisterSetCount(); ++set_idx)
270 {
271 const RegisterSet *set = real_registers_sp->GetRegisterSet(set_idx);
272 ConstString name(set->name);
273 for (size_t reg_idx = 0; reg_idx < set->num_registers; ++reg_idx)
274 {
275 register_sets[reg_idx] = name;
276 }
277 }
278 TypeSP gobuf_sp = FindType(target_sp, "runtime.gobuf");
279 if (!gobuf_sp)
280 {
281 Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OS));
282
283 if (log)
284 log->Printf("OperatingSystemGo unable to find struct Gobuf");
285 return false;
286 }
287 CompilerType gobuf_type(gobuf_sp->GetLayoutCompilerType());
288 for (size_t idx = 0; idx < real_registers_sp->GetRegisterCount(); ++idx)
289 {
290 RegisterInfo reg = *real_registers_sp->GetRegisterInfoAtIndex(idx);
291 int field_index = -1;
292 if (reg.kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_SP)
293 {
294 field_index = 0;
295 }
296 else if (reg.kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_PC)
297 {
298 field_index = 1;
299 }
300 if (field_index == -1)
301 {
302 reg.byte_offset = ~0;
303 }
304 else
305 {
306 std::string field_name;
307 uint64_t bit_offset = 0;
308 CompilerType field_type =
309 gobuf_type.GetFieldAtIndex(field_index, field_name, &bit_offset, nullptr, nullptr);
310 reg.byte_size = field_type.GetByteSize(nullptr);
311 reg.byte_offset = bit_offset / 8;
312 }
313 ConstString name(reg.name);
314 ConstString alt_name(reg.alt_name);
315 m_reginfo->AddRegister(reg, name, alt_name, register_sets[idx]);
316 }
317 return true;
318}
319
320//------------------------------------------------------------------
321// PluginInterface protocol
322//------------------------------------------------------------------
323ConstString
324OperatingSystemGo::GetPluginName()
325{
326 return GetPluginNameStatic();
327}
328
329uint32_t
330OperatingSystemGo::GetPluginVersion()
331{
332 return 1;
333}
334
335bool
336OperatingSystemGo::UpdateThreadList(ThreadList &old_thread_list, ThreadList &real_thread_list,
337 ThreadList &new_thread_list)
338{
339 new_thread_list = real_thread_list;
340 Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OS));
341
342 if (!(m_allg_sp || Init(real_thread_list)) || (m_allg_sp && !m_allglen_sp) ||
343 !GetGlobalPluginProperties()->GetEnableGoroutines())
344 {
345 return new_thread_list.GetSize(false) > 0;
346 }
347
348 if (log)
349 log->Printf("OperatingSystemGo::UpdateThreadList(%d, %d, %d) fetching thread data from Go for pid %" PRIu64,
350 old_thread_list.GetSize(false), real_thread_list.GetSize(false), new_thread_list.GetSize(0),
351 m_process->GetID());
352 uint64_t allglen = m_allglen_sp->GetValueAsUnsigned(0);
353 if (allglen == 0)
354 {
355 return new_thread_list.GetSize(false) > 0;
356 }
357 std::vector<Goroutine> goroutines;
358 // The threads that are in "new_thread_list" upon entry are the threads from the
359 // lldb_private::Process subclass, no memory threads will be in this list.
360
361 Error err;
362 for (uint64_t i = 0; i < allglen; ++i)
363 {
364 goroutines.push_back(CreateGoroutineAtIndex(i, err));
365 if (err.Fail())
366 {
367 err.PutToLog(log, "OperatingSystemGo::UpdateThreadList");
368 return new_thread_list.GetSize(false) > 0;
369 }
370 }
371 // Make a map so we can match goroutines with backing threads.
372 std::map<uint64_t, ThreadSP> stack_map;
373 for (uint32_t i = 0; i < real_thread_list.GetSize(false); ++i)
374 {
375 ThreadSP thread = real_thread_list.GetThreadAtIndex(i, false);
376 stack_map[thread->GetRegisterContext()->GetSP()] = thread;
377 }
378 for (const Goroutine &goroutine : goroutines)
379 {
380 if (0 /* Gidle */ == goroutine.m_status || 6 /* Gdead */ == goroutine.m_status)
381 {
382 continue;
383 }
384 ThreadSP memory_thread = old_thread_list.FindThreadByID(goroutine.m_goid, false);
385 if (memory_thread && IsOperatingSystemPluginThread(memory_thread) && memory_thread->IsValid())
386 {
387 memory_thread->ClearBackingThread();
388 }
389 else
390 {
391 memory_thread.reset(new ThreadMemory(*m_process, goroutine.m_goid, nullptr, nullptr, goroutine.m_gobuf));
392 }
393 // Search for the backing thread if the goroutine is running.
394 if (2 == (goroutine.m_status & 0xfff))
395 {
396 auto backing_it = stack_map.lower_bound(goroutine.m_lostack);
397 if (backing_it != stack_map.end())
398 {
399 if (goroutine.m_histack >= backing_it->first)
400 {
401 if (log)
402 log->Printf("OperatingSystemGo::UpdateThreadList found backing thread %" PRIx64 " (%" PRIx64
403 ") for thread %" PRIx64 "",
404 backing_it->second->GetID(), backing_it->second->GetProtocolID(),
405 memory_thread->GetID());
406 memory_thread->SetBackingThread(backing_it->second);
407 new_thread_list.RemoveThreadByID(backing_it->second->GetID(), false);
408 }
409 }
410 }
411 new_thread_list.AddThread(memory_thread);
412 }
413
414 return new_thread_list.GetSize(false) > 0;
415}
416
417void
418OperatingSystemGo::ThreadWasSelected(Thread *thread)
419{
420}
421
422RegisterContextSP
423OperatingSystemGo::CreateRegisterContextForThread(Thread *thread, addr_t reg_data_addr)
424{
425 RegisterContextSP reg_ctx_sp;
426 if (!thread)
427 return reg_ctx_sp;
428
429 if (!IsOperatingSystemPluginThread(thread->shared_from_this()))
430 return reg_ctx_sp;
431
432 reg_ctx_sp.reset(new RegisterContextGo(*thread, 0, *m_reginfo, reg_data_addr));
433 return reg_ctx_sp;
434}
435
436StopInfoSP
437OperatingSystemGo::CreateThreadStopReason(lldb_private::Thread *thread)
438{
439 StopInfoSP stop_info_sp;
440 return stop_info_sp;
441}
442
443lldb::ThreadSP
444OperatingSystemGo::CreateThread(lldb::tid_t tid, addr_t context)
445{
446 Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OS));
447
448 if (log)
449 log->Printf("OperatingSystemGo::CreateThread (tid = 0x%" PRIx64 ", context = 0x%" PRIx64 ") not implemented",
450 tid, context);
451
452 return ThreadSP();
453}
454
455ValueObjectSP
456OperatingSystemGo::FindGlobal(TargetSP target, const char *name)
457{
458 VariableList variable_list;
459 const bool append = true;
460
461 Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OS));
462
463 if (log)
464 {
465 log->Printf("exe: %s", target->GetExecutableModule()->GetSpecificationDescription().c_str());
466 log->Printf("modules: %zu", target->GetImages().GetSize());
467 }
468
469 uint32_t match_count = target->GetImages().FindGlobalVariables(ConstString(name), append, 1, variable_list);
470 if (match_count > 0)
471 {
472 ExecutionContextScope *exe_scope = target->GetProcessSP().get();
473 if (exe_scope == NULL)
474 exe_scope = target.get();
475 return ValueObjectVariable::Create(exe_scope, variable_list.GetVariableAtIndex(0));
476 }
477 return ValueObjectSP();
478}
479
480TypeSP
481OperatingSystemGo::FindType(TargetSP target_sp, const char *name)
482{
483 ConstString const_typename(name);
484 SymbolContext sc;
485 const bool exact_match = false;
486
487 const ModuleList &module_list = target_sp->GetImages();
488 size_t count = module_list.GetSize();
489 for (size_t idx = 0; idx < count; idx++)
490 {
491 ModuleSP module_sp(module_list.GetModuleAtIndex(idx));
492 if (module_sp)
493 {
494 TypeSP type_sp(module_sp->FindFirstType(sc, const_typename, exact_match));
495 if (type_sp)
496 return type_sp;
497 }
498 }
499 Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OS));
500
501 if (log)
502 log->Printf("OperatingSystemGo::FindType(%s): not found", name);
503 return TypeSP();
504}
505
506OperatingSystemGo::Goroutine
507OperatingSystemGo::CreateGoroutineAtIndex(uint64_t idx, Error &err)
508{
509 err.Clear();
510 Goroutine result;
511 ValueObjectSP g = m_allg_sp->GetSyntheticArrayMember(idx, true)->Dereference(err);
512 if (err.Fail())
513 {
514 return result;
515 }
516
517 ConstString name("goid");
518 ValueObjectSP val = g->GetChildMemberWithName(name, true);
519 bool success = false;
520 result.m_goid = val->GetValueAsUnsigned(0, &success);
521 if (!success)
522 {
523 err.SetErrorToGenericError();
524 err.SetErrorString("unable to read goid");
525 return result;
526 }
527 name.SetCString("atomicstatus");
528 val = g->GetChildMemberWithName(name, true);
529 result.m_status = (uint32_t)val->GetValueAsUnsigned(0, &success);
530 if (!success)
531 {
532 err.SetErrorToGenericError();
533 err.SetErrorString("unable to read atomicstatus");
534 return result;
535 }
536 name.SetCString("sched");
537 val = g->GetChildMemberWithName(name, true);
538 result.m_gobuf = val->GetAddressOf(false);
539 name.SetCString("stack");
540 val = g->GetChildMemberWithName(name, true);
541 name.SetCString("lo");
542 ValueObjectSP child = val->GetChildMemberWithName(name, true);
543 result.m_lostack = child->GetValueAsUnsigned(0, &success);
544 if (!success)
545 {
546 err.SetErrorToGenericError();
547 err.SetErrorString("unable to read stack.lo");
548 return result;
549 }
550 name.SetCString("hi");
551 child = val->GetChildMemberWithName(name, true);
552 result.m_histack = child->GetValueAsUnsigned(0, &success);
553 if (!success)
554 {
555 err.SetErrorToGenericError();
556 err.SetErrorString("unable to read stack.hi");
557 return result;
558 }
559 return result;
560}