aboutsummaryrefslogtreecommitdiff
path: root/source/Plugins/OperatingSystem/Go/OperatingSystemGo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/Plugins/OperatingSystem/Go/OperatingSystemGo.cpp')
-rw-r--r--source/Plugins/OperatingSystem/Go/OperatingSystemGo.cpp559
1 files changed, 559 insertions, 0 deletions
diff --git a/source/Plugins/OperatingSystem/Go/OperatingSystemGo.cpp b/source/Plugins/OperatingSystem/Go/OperatingSystemGo.cpp
new file mode 100644
index 000000000000..86c574f2776c
--- /dev/null
+++ b/source/Plugins/OperatingSystem/Go/OperatingSystemGo.cpp
@@ -0,0 +1,559 @@
+//===-- OperatingSystemGo.cpp -----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// C Includes
+// C++ Includes
+#include <unordered_map>
+
+// Other libraries and framework includes
+// Project includes
+#include "OperatingSystemGo.h"
+
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/RegisterValue.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Interpreter/OptionGroupBoolean.h"
+#include "lldb/Interpreter/OptionGroupUInt64.h"
+#include "lldb/Interpreter/Property.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadList.h"
+#include "lldb/Target/Thread.h"
+#include "Plugins/Process/Utility/DynamicRegisterInfo.h"
+#include "Plugins/Process/Utility/RegisterContextMemory.h"
+#include "Plugins/Process/Utility/ThreadMemory.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace
+{
+
+static PropertyDefinition g_properties[] = {{"enable", OptionValue::eTypeBoolean, true, true, nullptr, nullptr,
+ "Specify whether goroutines should be treated as threads."},
+ {NULL, OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL}};
+
+enum
+{
+ ePropertyEnableGoroutines,
+};
+
+class PluginProperties : public Properties
+{
+public:
+ PluginProperties()
+ : Properties()
+ {
+ m_collection_sp.reset(new OptionValueProperties(GetSettingName()));
+ m_collection_sp->Initialize(g_properties);
+ }
+
+ ~PluginProperties() override = default;
+
+ static ConstString
+ GetSettingName()
+ {
+ return OperatingSystemGo::GetPluginNameStatic();
+ }
+
+ bool
+ GetEnableGoroutines()
+ {
+ const uint32_t idx = ePropertyEnableGoroutines;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(NULL, idx, g_properties[idx].default_uint_value);
+ }
+
+ bool
+ SetEnableGoroutines(bool enable)
+ {
+ const uint32_t idx = ePropertyEnableGoroutines;
+ return m_collection_sp->SetPropertyAtIndexAsUInt64(NULL, idx, enable);
+ }
+};
+
+typedef std::shared_ptr<PluginProperties> OperatingSystemGoPropertiesSP;
+
+static const OperatingSystemGoPropertiesSP &
+GetGlobalPluginProperties()
+{
+ static OperatingSystemGoPropertiesSP g_settings_sp;
+ if (!g_settings_sp)
+ g_settings_sp.reset(new PluginProperties());
+ return g_settings_sp;
+}
+
+class RegisterContextGo : public RegisterContextMemory
+{
+public:
+ RegisterContextGo(lldb_private::Thread &thread, uint32_t concrete_frame_idx, DynamicRegisterInfo &reg_info,
+ lldb::addr_t reg_data_addr)
+ : RegisterContextMemory(thread, concrete_frame_idx, reg_info, reg_data_addr)
+ {
+ const RegisterInfo *sp = reg_info.GetRegisterInfoAtIndex(
+ reg_info.ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP));
+ const RegisterInfo *pc = reg_info.GetRegisterInfoAtIndex(
+ reg_info.ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC));
+ size_t byte_size = std::max(sp->byte_offset + sp->byte_size, pc->byte_offset + pc->byte_size);
+
+ DataBufferSP reg_data_sp(new DataBufferHeap(byte_size, 0));
+ m_reg_data.SetData(reg_data_sp);
+ }
+
+ ~RegisterContextGo() override = default;
+
+ bool
+ ReadRegister(const lldb_private::RegisterInfo *reg_info,
+ lldb_private::RegisterValue &reg_value) override
+ {
+ switch (reg_info->kinds[eRegisterKindGeneric])
+ {
+ case LLDB_REGNUM_GENERIC_SP:
+ case LLDB_REGNUM_GENERIC_PC:
+ return RegisterContextMemory::ReadRegister(reg_info, reg_value);
+ default:
+ reg_value.SetValueToInvalid();
+ return true;
+ }
+ }
+
+ bool
+ WriteRegister(const lldb_private::RegisterInfo *reg_info,
+ const lldb_private::RegisterValue &reg_value) override
+ {
+ switch (reg_info->kinds[eRegisterKindGeneric])
+ {
+ case LLDB_REGNUM_GENERIC_SP:
+ case LLDB_REGNUM_GENERIC_PC:
+ return RegisterContextMemory::WriteRegister(reg_info, reg_value);
+ default:
+ return false;
+ }
+ }
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(RegisterContextGo);
+};
+
+} // anonymous namespace
+
+struct OperatingSystemGo::Goroutine
+{
+ uint64_t m_lostack;
+ uint64_t m_histack;
+ uint64_t m_goid;
+ addr_t m_gobuf;
+ uint32_t m_status;
+};
+
+void
+OperatingSystemGo::Initialize()
+{
+ PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance,
+ DebuggerInitialize);
+}
+
+void
+OperatingSystemGo::DebuggerInitialize(Debugger &debugger)
+{
+ if (!PluginManager::GetSettingForOperatingSystemPlugin(debugger, PluginProperties::GetSettingName()))
+ {
+ const bool is_global_setting = true;
+ PluginManager::CreateSettingForOperatingSystemPlugin(
+ debugger, GetGlobalPluginProperties()->GetValueProperties(),
+ ConstString("Properties for the goroutine thread plug-in."), is_global_setting);
+ }
+}
+
+void
+OperatingSystemGo::Terminate()
+{
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+OperatingSystem *
+OperatingSystemGo::CreateInstance(Process *process, bool force)
+{
+ if (!force)
+ {
+ TargetSP target_sp = process->CalculateTarget();
+ if (!target_sp)
+ return nullptr;
+ ModuleList &module_list = target_sp->GetImages();
+ Mutex::Locker modules_locker(module_list.GetMutex());
+ const size_t num_modules = module_list.GetSize();
+ bool found_go_runtime = false;
+ for (size_t i = 0; i < num_modules; ++i)
+ {
+ Module *module = module_list.GetModulePointerAtIndexUnlocked(i);
+ const SectionList *section_list = module->GetSectionList();
+ if (section_list)
+ {
+ SectionSP section_sp(section_list->FindSectionByType(eSectionTypeGoSymtab, true));
+ if (section_sp)
+ {
+ found_go_runtime = true;
+ break;
+ }
+ }
+ }
+ if (!found_go_runtime)
+ return nullptr;
+ }
+ return new OperatingSystemGo(process);
+}
+
+OperatingSystemGo::OperatingSystemGo(lldb_private::Process *process)
+ : OperatingSystem(process)
+ , m_reginfo(new DynamicRegisterInfo)
+{
+}
+
+OperatingSystemGo::~OperatingSystemGo() = default;
+
+ConstString
+OperatingSystemGo::GetPluginNameStatic()
+{
+ static ConstString g_name("goroutines");
+ return g_name;
+}
+
+const char *
+OperatingSystemGo::GetPluginDescriptionStatic()
+{
+ return "Operating system plug-in that reads runtime data-structures for goroutines.";
+}
+
+bool
+OperatingSystemGo::Init(ThreadList &threads)
+{
+ if (threads.GetSize(false) < 1)
+ return false;
+ TargetSP target_sp = m_process->CalculateTarget();
+ if (!target_sp)
+ return false;
+ m_allg_sp = FindGlobal(target_sp, "runtime.allg");
+ m_allglen_sp = FindGlobal(target_sp, "runtime.allglen");
+
+ if (m_allg_sp && !m_allglen_sp)
+ {
+ StreamSP error_sp = target_sp->GetDebugger().GetAsyncErrorStream();
+ error_sp->Printf("Unsupported Go runtime version detected.");
+ return false;
+ }
+
+ if (!m_allg_sp)
+ return false;
+
+ RegisterContextSP real_registers_sp = threads.GetThreadAtIndex(0, false)->GetRegisterContext();
+
+ std::unordered_map<size_t, ConstString> register_sets;
+ for (size_t set_idx = 0; set_idx < real_registers_sp->GetRegisterSetCount(); ++set_idx)
+ {
+ const RegisterSet *set = real_registers_sp->GetRegisterSet(set_idx);
+ ConstString name(set->name);
+ for (size_t reg_idx = 0; reg_idx < set->num_registers; ++reg_idx)
+ {
+ register_sets[reg_idx] = name;
+ }
+ }
+ TypeSP gobuf_sp = FindType(target_sp, "runtime.gobuf");
+ if (!gobuf_sp)
+ {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OS));
+
+ if (log)
+ log->Printf("OperatingSystemGo unable to find struct Gobuf");
+ return false;
+ }
+ CompilerType gobuf_type(gobuf_sp->GetLayoutCompilerType());
+ for (size_t idx = 0; idx < real_registers_sp->GetRegisterCount(); ++idx)
+ {
+ RegisterInfo reg = *real_registers_sp->GetRegisterInfoAtIndex(idx);
+ int field_index = -1;
+ if (reg.kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_SP)
+ {
+ field_index = 0;
+ }
+ else if (reg.kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_PC)
+ {
+ field_index = 1;
+ }
+ if (field_index == -1)
+ {
+ reg.byte_offset = ~0;
+ }
+ else
+ {
+ std::string field_name;
+ uint64_t bit_offset = 0;
+ CompilerType field_type =
+ gobuf_type.GetFieldAtIndex(field_index, field_name, &bit_offset, nullptr, nullptr);
+ reg.byte_size = field_type.GetByteSize(nullptr);
+ reg.byte_offset = bit_offset / 8;
+ }
+ ConstString name(reg.name);
+ ConstString alt_name(reg.alt_name);
+ m_reginfo->AddRegister(reg, name, alt_name, register_sets[idx]);
+ }
+ return true;
+}
+
+//------------------------------------------------------------------
+// PluginInterface protocol
+//------------------------------------------------------------------
+ConstString
+OperatingSystemGo::GetPluginName()
+{
+ return GetPluginNameStatic();
+}
+
+uint32_t
+OperatingSystemGo::GetPluginVersion()
+{
+ return 1;
+}
+
+bool
+OperatingSystemGo::UpdateThreadList(ThreadList &old_thread_list, ThreadList &real_thread_list,
+ ThreadList &new_thread_list)
+{
+ new_thread_list = real_thread_list;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OS));
+
+ if (!(m_allg_sp || Init(real_thread_list)) || (m_allg_sp && !m_allglen_sp) ||
+ !GetGlobalPluginProperties()->GetEnableGoroutines())
+ {
+ return new_thread_list.GetSize(false) > 0;
+ }
+
+ if (log)
+ log->Printf("OperatingSystemGo::UpdateThreadList(%d, %d, %d) fetching thread data from Go for pid %" PRIu64,
+ old_thread_list.GetSize(false), real_thread_list.GetSize(false), new_thread_list.GetSize(0),
+ m_process->GetID());
+ uint64_t allglen = m_allglen_sp->GetValueAsUnsigned(0);
+ if (allglen == 0)
+ {
+ return new_thread_list.GetSize(false) > 0;
+ }
+ std::vector<Goroutine> goroutines;
+ // The threads that are in "new_thread_list" upon entry are the threads from the
+ // lldb_private::Process subclass, no memory threads will be in this list.
+
+ Error err;
+ for (uint64_t i = 0; i < allglen; ++i)
+ {
+ goroutines.push_back(CreateGoroutineAtIndex(i, err));
+ if (err.Fail())
+ {
+ err.PutToLog(log, "OperatingSystemGo::UpdateThreadList");
+ return new_thread_list.GetSize(false) > 0;
+ }
+ }
+ // Make a map so we can match goroutines with backing threads.
+ std::map<uint64_t, ThreadSP> stack_map;
+ for (uint32_t i = 0; i < real_thread_list.GetSize(false); ++i)
+ {
+ ThreadSP thread = real_thread_list.GetThreadAtIndex(i, false);
+ stack_map[thread->GetRegisterContext()->GetSP()] = thread;
+ }
+ for (const Goroutine &goroutine : goroutines)
+ {
+ if (0 /* Gidle */ == goroutine.m_status || 6 /* Gdead */ == goroutine.m_status)
+ {
+ continue;
+ }
+ ThreadSP memory_thread = old_thread_list.FindThreadByID(goroutine.m_goid, false);
+ if (memory_thread && IsOperatingSystemPluginThread(memory_thread) && memory_thread->IsValid())
+ {
+ memory_thread->ClearBackingThread();
+ }
+ else
+ {
+ memory_thread.reset(new ThreadMemory(*m_process, goroutine.m_goid, nullptr, nullptr, goroutine.m_gobuf));
+ }
+ // Search for the backing thread if the goroutine is running.
+ if (2 == (goroutine.m_status & 0xfff))
+ {
+ auto backing_it = stack_map.lower_bound(goroutine.m_lostack);
+ if (backing_it != stack_map.end())
+ {
+ if (goroutine.m_histack >= backing_it->first)
+ {
+ if (log)
+ log->Printf("OperatingSystemGo::UpdateThreadList found backing thread %" PRIx64 " (%" PRIx64
+ ") for thread %" PRIx64 "",
+ backing_it->second->GetID(), backing_it->second->GetProtocolID(),
+ memory_thread->GetID());
+ memory_thread->SetBackingThread(backing_it->second);
+ new_thread_list.RemoveThreadByID(backing_it->second->GetID(), false);
+ }
+ }
+ }
+ new_thread_list.AddThread(memory_thread);
+ }
+
+ return new_thread_list.GetSize(false) > 0;
+}
+
+void
+OperatingSystemGo::ThreadWasSelected(Thread *thread)
+{
+}
+
+RegisterContextSP
+OperatingSystemGo::CreateRegisterContextForThread(Thread *thread, addr_t reg_data_addr)
+{
+ RegisterContextSP reg_ctx_sp;
+ if (!thread)
+ return reg_ctx_sp;
+
+ if (!IsOperatingSystemPluginThread(thread->shared_from_this()))
+ return reg_ctx_sp;
+
+ reg_ctx_sp.reset(new RegisterContextGo(*thread, 0, *m_reginfo, reg_data_addr));
+ return reg_ctx_sp;
+}
+
+StopInfoSP
+OperatingSystemGo::CreateThreadStopReason(lldb_private::Thread *thread)
+{
+ StopInfoSP stop_info_sp;
+ return stop_info_sp;
+}
+
+lldb::ThreadSP
+OperatingSystemGo::CreateThread(lldb::tid_t tid, addr_t context)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OS));
+
+ if (log)
+ log->Printf("OperatingSystemGo::CreateThread (tid = 0x%" PRIx64 ", context = 0x%" PRIx64 ") not implemented",
+ tid, context);
+
+ return ThreadSP();
+}
+
+ValueObjectSP
+OperatingSystemGo::FindGlobal(TargetSP target, const char *name)
+{
+ VariableList variable_list;
+ const bool append = true;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OS));
+
+ if (log)
+ {
+ log->Printf("exe: %s", target->GetExecutableModule()->GetSpecificationDescription().c_str());
+ log->Printf("modules: %zu", target->GetImages().GetSize());
+ }
+
+ uint32_t match_count = target->GetImages().FindGlobalVariables(ConstString(name), append, 1, variable_list);
+ if (match_count > 0)
+ {
+ ExecutionContextScope *exe_scope = target->GetProcessSP().get();
+ if (exe_scope == NULL)
+ exe_scope = target.get();
+ return ValueObjectVariable::Create(exe_scope, variable_list.GetVariableAtIndex(0));
+ }
+ return ValueObjectSP();
+}
+
+TypeSP
+OperatingSystemGo::FindType(TargetSP target_sp, const char *name)
+{
+ ConstString const_typename(name);
+ SymbolContext sc;
+ const bool exact_match = false;
+
+ const ModuleList &module_list = target_sp->GetImages();
+ size_t count = module_list.GetSize();
+ for (size_t idx = 0; idx < count; idx++)
+ {
+ ModuleSP module_sp(module_list.GetModuleAtIndex(idx));
+ if (module_sp)
+ {
+ TypeSP type_sp(module_sp->FindFirstType(sc, const_typename, exact_match));
+ if (type_sp)
+ return type_sp;
+ }
+ }
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OS));
+
+ if (log)
+ log->Printf("OperatingSystemGo::FindType(%s): not found", name);
+ return TypeSP();
+}
+
+OperatingSystemGo::Goroutine
+OperatingSystemGo::CreateGoroutineAtIndex(uint64_t idx, Error &err)
+{
+ err.Clear();
+ Goroutine result;
+ ValueObjectSP g = m_allg_sp->GetSyntheticArrayMember(idx, true)->Dereference(err);
+ if (err.Fail())
+ {
+ return result;
+ }
+
+ ConstString name("goid");
+ ValueObjectSP val = g->GetChildMemberWithName(name, true);
+ bool success = false;
+ result.m_goid = val->GetValueAsUnsigned(0, &success);
+ if (!success)
+ {
+ err.SetErrorToGenericError();
+ err.SetErrorString("unable to read goid");
+ return result;
+ }
+ name.SetCString("atomicstatus");
+ val = g->GetChildMemberWithName(name, true);
+ result.m_status = (uint32_t)val->GetValueAsUnsigned(0, &success);
+ if (!success)
+ {
+ err.SetErrorToGenericError();
+ err.SetErrorString("unable to read atomicstatus");
+ return result;
+ }
+ name.SetCString("sched");
+ val = g->GetChildMemberWithName(name, true);
+ result.m_gobuf = val->GetAddressOf(false);
+ name.SetCString("stack");
+ val = g->GetChildMemberWithName(name, true);
+ name.SetCString("lo");
+ ValueObjectSP child = val->GetChildMemberWithName(name, true);
+ result.m_lostack = child->GetValueAsUnsigned(0, &success);
+ if (!success)
+ {
+ err.SetErrorToGenericError();
+ err.SetErrorString("unable to read stack.lo");
+ return result;
+ }
+ name.SetCString("hi");
+ child = val->GetChildMemberWithName(name, true);
+ result.m_histack = child->GetValueAsUnsigned(0, &success);
+ if (!success)
+ {
+ err.SetErrorToGenericError();
+ err.SetErrorString("unable to read stack.hi");
+ return result;
+ }
+ return result;
+}