Index: cmake/LLDBDependencies.cmake =================================================================== --- cmake/LLDBDependencies.cmake +++ cmake/LLDBDependencies.cmake @@ -48,6 +48,7 @@ lldbPluginUnwindAssemblyX86 lldbPluginAppleObjCRuntime lldbPluginRenderScriptRuntime + lldbPluginLanguageRuntimeGo lldbPluginCXXItaniumABI lldbPluginABIMacOSX_arm lldbPluginABIMacOSX_arm64 Index: lldb.xcodeproj/project.pbxproj =================================================================== --- lldb.xcodeproj/project.pbxproj +++ lldb.xcodeproj/project.pbxproj @@ -819,6 +819,7 @@ 9AC703AF117675410086C050 /* SBInstruction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9AC703AE117675410086C050 /* SBInstruction.cpp */; }; 9AC703B1117675490086C050 /* SBInstructionList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9AC703B0117675490086C050 /* SBInstructionList.cpp */; }; A36FF33C17D8E94600244D40 /* OptionParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A36FF33B17D8E94600244D40 /* OptionParser.cpp */; }; + AE44FB3E1BB485960033EB62 /* GoLanguageRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE44FB3D1BB485960033EB62 /* GoLanguageRuntime.cpp */; }; AE6897281B94F6DE0018845D /* DWARFASTParserGo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE6897261B94F6DE0018845D /* DWARFASTParserGo.cpp */; }; AE7F56291B8FE418001377A8 /* GoASTContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AEFFBA7C1AC4835D0087B932 /* GoASTContext.cpp */; }; AE8F624919EF3E1E00326B21 /* OperatingSystemGo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE8F624719EF3E1E00326B21 /* OperatingSystemGo.cpp */; }; @@ -2630,6 +2631,8 @@ 9AF16CC7114086A1007A7B3F /* SBBreakpointLocation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBBreakpointLocation.cpp; path = source/API/SBBreakpointLocation.cpp; sourceTree = ""; }; A36FF33B17D8E94600244D40 /* OptionParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OptionParser.cpp; sourceTree = ""; }; A36FF33D17D8E98800244D40 /* OptionParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionParser.h; path = include/lldb/Host/OptionParser.h; sourceTree = ""; }; + AE44FB3C1BB4858A0033EB62 /* GoLanguageRuntime.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = GoLanguageRuntime.h; path = Go/GoLanguageRuntime.h; sourceTree = ""; }; + AE44FB3D1BB485960033EB62 /* GoLanguageRuntime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GoLanguageRuntime.cpp; path = Go/GoLanguageRuntime.cpp; sourceTree = ""; }; AE6897261B94F6DE0018845D /* DWARFASTParserGo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DWARFASTParserGo.cpp; sourceTree = ""; }; AE6897271B94F6DE0018845D /* DWARFASTParserGo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DWARFASTParserGo.h; sourceTree = ""; }; AE8F624719EF3E1E00326B21 /* OperatingSystemGo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OperatingSystemGo.cpp; path = Go/OperatingSystemGo.cpp; sourceTree = ""; }; @@ -5119,6 +5122,7 @@ 4CCA643A13B40B82003BDF98 /* LanguageRuntime */ = { isa = PBXGroup; children = ( + AE44FB3B1BB485730033EB62 /* Go */, 49724D961AD6ECFA0033C538 /* RenderScript */, 4CCA643B13B40B82003BDF98 /* CPlusPlus */, 4CCA644013B40B82003BDF98 /* ObjC */, @@ -5442,6 +5446,15 @@ name = "SysV-mips"; sourceTree = ""; }; + AE44FB3B1BB485730033EB62 /* Go */ = { + isa = PBXGroup; + children = ( + AE44FB3C1BB4858A0033EB62 /* GoLanguageRuntime.h */, + AE44FB3D1BB485960033EB62 /* GoLanguageRuntime.cpp */, + ); + name = Go; + sourceTree = ""; + }; AE8F624519EF3DFC00326B21 /* Go */ = { isa = PBXGroup; children = ( @@ -6278,6 +6291,7 @@ 266E829D1B8E542C008FCA06 /* DWARFAttribute.cpp in Sources */, 2689004613353E0400698AC0 /* ModuleList.cpp in Sources */, 2689004713353E0400698AC0 /* PluginManager.cpp in Sources */, + AE44FB3E1BB485960033EB62 /* GoLanguageRuntime.cpp in Sources */, AF0C112818580CD800C4C45B /* QueueItem.cpp in Sources */, AF254E31170CCC33007AE5C9 /* PlatformDarwinKernel.cpp in Sources */, 2689004813353E0400698AC0 /* RegularExpression.cpp in Sources */, Index: source/API/SystemInitializerFull.cpp =================================================================== --- source/API/SystemInitializerFull.cpp +++ source/API/SystemInitializerFull.cpp @@ -49,6 +49,7 @@ #include "Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h" #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h" #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h" +#include "Plugins/LanguageRuntime/Go/GoLanguageRuntime.h" #include "Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h" #include "Plugins/MemoryHistory/asan/MemoryHistoryASan.h" #include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h" @@ -289,6 +290,7 @@ AppleObjCRuntimeV1::Initialize(); SystemRuntimeMacOSX::Initialize(); RenderScriptRuntime::Initialize(); + GoLanguageRuntime::Initialize(); CPlusPlusLanguage::Initialize(); ObjCLanguage::Initialize(); Index: source/Plugins/LanguageRuntime/CMakeLists.txt =================================================================== --- source/Plugins/LanguageRuntime/CMakeLists.txt +++ source/Plugins/LanguageRuntime/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(CPlusPlus) add_subdirectory(ObjC) +add_subdirectory(Go) add_subdirectory(RenderScript) Index: source/Plugins/LanguageRuntime/Go/CMakeLists.txt =================================================================== --- /dev/null +++ source/Plugins/LanguageRuntime/Go/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_NO_RTTI 1) + +add_lldb_library(lldbPluginLanguageRuntimeGo + GoLanguageRuntime.cpp + ) Index: source/Plugins/LanguageRuntime/Go/GoLanguageRuntime.h =================================================================== --- /dev/null +++ source/Plugins/LanguageRuntime/Go/GoLanguageRuntime.h @@ -0,0 +1,93 @@ +//===-- GoLanguageRuntime.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_GoLanguageRuntime_h_ +#define liblldb_GoLanguageRuntime_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Breakpoint/BreakpointResolver.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/Core/Value.h" + +namespace lldb_private { + + class GoLanguageRuntime : + public lldb_private::LanguageRuntime + { + public: + ~GoLanguageRuntime() { } + + lldb::LanguageType + GetLanguageType() const override + { + return lldb::eLanguageTypeGo; + } + + bool + GetObjectDescription(Stream &str, ValueObject &object) override + { + // TODO(ribrdb): Maybe call String() method? + return false; + } + + bool + GetObjectDescription(Stream &str, Value &value, ExecutionContextScope *exe_scope) override + { + return false; + } + + bool GetDynamicTypeAndAddress(ValueObject &in_value, lldb::DynamicValueType use_dynamic, + TypeAndOrName &class_type_or_name, Address &address, + Value::ValueType &value_type) override; + + bool CouldHaveDynamicValue(ValueObject &in_value) override; + + lldb::BreakpointResolverSP + CreateExceptionResolver(Breakpoint *bkpt, bool catch_bp, bool throw_bp) override + { + return lldb::BreakpointResolverSP(); + } + + TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name, ValueObject &static_value) override; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::LanguageRuntime * + CreateInstance (Process *process, lldb::LanguageType language); + + static lldb_private::ConstString + GetPluginNameStatic(); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + + private: + GoLanguageRuntime(Process *process) : lldb_private::LanguageRuntime(process) { } // Call CreateInstance instead. + }; + +} // namespace lldb_private + +#endif // liblldb_GoLanguageRuntime_h_ Index: source/Plugins/LanguageRuntime/Go/GoLanguageRuntime.cpp =================================================================== --- /dev/null +++ source/Plugins/LanguageRuntime/Go/GoLanguageRuntime.cpp @@ -0,0 +1,238 @@ +//===-- GoLanguageRuntime.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "GoLanguageRuntime.h" + +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/Symbol/GoASTContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "llvm/ADT/Twine.h" + +#include + +using namespace lldb; +using namespace lldb_private; + +namespace { +ValueObjectSP GetChild(ValueObject& obj, const char* name, bool dereference = true) { + ConstString name_const_str(name); + ValueObjectSP result = obj.GetChildMemberWithName(name_const_str, true); + if (dereference && result && result->IsPointerType()) { + Error err; + result = result->Dereference(err); + if (err.Fail()) + result.reset(); + } + return result; +} + +ConstString ReadString(ValueObject& str, Process* process) { + ConstString result; + ValueObjectSP data = GetChild(str, "str", false); + ValueObjectSP len = GetChild(str, "len"); + if (len && data) + { + Error err; + lldb::addr_t addr = data->GetPointerValue(); + if (addr == LLDB_INVALID_ADDRESS) + return result; + uint64_t byte_size = len->GetValueAsUnsigned(0); + char* buf = new char[byte_size + 1]; + buf[byte_size] = 0; + size_t bytes_read = process->ReadMemory (addr, + buf, + byte_size, + err); + if (!(err.Fail() || bytes_read != byte_size)) + result = ConstString(buf, bytes_read); + delete[] buf; + } + return result; +} + +ConstString +ReadTypeName(ValueObjectSP type, Process* process) +{ + if (ValueObjectSP uncommon = GetChild(*type, "x")) + { + ValueObjectSP name = GetChild(*uncommon, "name"); + ValueObjectSP package = GetChild(*uncommon, "pkgpath"); + if (name && name->GetPointerValue() != 0 && package && package->GetPointerValue() != 0) + { + ConstString package_const_str = ReadString(*package, process); + ConstString name_const_str = ReadString(*name, process); + if (package_const_str.GetLength() == 0) + return name_const_str; + return ConstString((package_const_str.GetStringRef() + "." + name_const_str.GetStringRef()).str()); + } + } + ValueObjectSP name = GetChild(*type, "_string"); + if (name) + return ReadString(*name, process); + return ConstString(""); +} + +CompilerType +LookupRuntimeType(ValueObjectSP type, ExecutionContext* exe_ctx, bool* is_direct) +{ + uint8_t kind = GetChild(*type, "kind")->GetValueAsUnsigned(0); + *is_direct = GoASTContext::IsDirectIface(kind); + if (GoASTContext::IsPointerKind(kind)) + { + CompilerType type_ptr = type->GetCompilerType().GetPointerType(); + Error err; + ValueObjectSP elem = type->CreateValueObjectFromAddress("elem", type->GetAddressOf() + type->GetByteSize(), *exe_ctx, type_ptr)->Dereference(err); + if (err.Fail()) + return CompilerType(); + bool tmp_direct; + return LookupRuntimeType(elem, exe_ctx, &tmp_direct).GetPointerType(); + } + Target *target = exe_ctx->GetTargetPtr(); + Process *process = exe_ctx->GetProcessPtr(); + + ConstString const_typename = ReadTypeName(type, process); + if (const_typename.GetLength() == 0) + return CompilerType(); + + SymbolContext sc; + TypeList type_list; + uint32_t num_matches = target->GetImages().FindTypes (sc, + const_typename, + false, + 2, + type_list); + if (num_matches > 0) { + return type_list.GetTypeAtIndex(0)->GetFullCompilerType(); + } + return CompilerType(); +} + +} + +bool +GoLanguageRuntime::CouldHaveDynamicValue (ValueObject &in_value) +{ + return GoASTContext::IsGoInterface(in_value.GetCompilerType()); +} + +bool +GoLanguageRuntime::GetDynamicTypeAndAddress(ValueObject &in_value, lldb::DynamicValueType use_dynamic, + TypeAndOrName &class_type_or_name, Address &dynamic_address, + Value::ValueType &value_type) +{ + value_type = Value::eValueTypeScalar; + class_type_or_name.Clear(); + if (CouldHaveDynamicValue (in_value)) + { + Error err; + ValueObjectSP iface = in_value.GetStaticValue(); + ValueObjectSP data_sp = GetChild(*iface, "data", false); + if (!data_sp) + return false; + + if (ValueObjectSP tab = GetChild(*iface, "tab")) + iface = tab; + ValueObjectSP type = GetChild(*iface, "_type"); + if (!type) + { + return false; + } + + bool direct; + ExecutionContext exe_ctx (in_value.GetExecutionContextRef()); + CompilerType final_type = LookupRuntimeType(type, &exe_ctx, &direct); + if (!final_type) + return false; + if (direct) + { + class_type_or_name.SetCompilerType(final_type); + } + else + { + // TODO: implement reference types or fix caller to support dynamic types that aren't pointers + // so we don't have to introduce this extra pointer. + class_type_or_name.SetCompilerType(final_type.GetPointerType()); + } + + dynamic_address.SetLoadAddress(data_sp->GetPointerValue(), exe_ctx.GetTargetPtr()); + + return true; + } + return false; +} + +TypeAndOrName +GoLanguageRuntime::FixUpDynamicType(const TypeAndOrName &type_and_or_name, ValueObject &static_value) +{ + return type_and_or_name; +} + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +LanguageRuntime * +GoLanguageRuntime::CreateInstance (Process *process, lldb::LanguageType language) +{ + if (language == eLanguageTypeGo) + return new GoLanguageRuntime (process); + else + return NULL; +} + +void +GoLanguageRuntime::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + "Go Language Runtime", + CreateInstance); +} + +void +GoLanguageRuntime::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +lldb_private::ConstString +GoLanguageRuntime::GetPluginNameStatic() +{ + static ConstString g_name("golang"); + return g_name; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +GoLanguageRuntime::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +GoLanguageRuntime::GetPluginVersion() +{ + return 1; +} + Index: source/Plugins/LanguageRuntime/Go/Makefile =================================================================== --- /dev/null +++ source/Plugins/LanguageRuntime/Go/Makefile @@ -0,0 +1,14 @@ +##===- Source/Plugins/LangRuntime/Go/Makefile ----*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginLanguageRuntimeGo +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile Index: source/Symbol/GoASTContext.cpp =================================================================== --- source/Symbol/GoASTContext.cpp +++ source/Symbol/GoASTContext.cpp @@ -1184,6 +1184,9 @@ uint32_t GoASTContext::GetIndexOfChildWithName(void *type, const char *name, bool omit_empty_base_classes) { + if (!type || !GetCompleteType(type)) + return UINT_MAX; + GoType *t = static_cast(type); GoStruct *s = t->GetStruct(); if (s) Index: test/lang/go/runtime/TestGoASTContext.py =================================================================== --- /dev/null +++ test/lang/go/runtime/TestGoASTContext.py @@ -0,0 +1,79 @@ +"""Test the go dynamic type handling.""" + +import os, time +import unittest2 +import lldb +import lldbutil +from lldbtest import * + +class TestGoLanguageRuntime(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @python_api_test + @skipIfRemote # Not remote test suite ready + @skipUnlessGoInstalled + def test_with_dsym_and_python_api(self): + """Test GoASTContext dwarf parsing.""" + self.buildGo() + self.launchProcess() + self.go_interface_types() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break inside main(). + self.main_source = "main.go" + self.break_line1 = line_number(self.main_source, '// Set breakpoint 1') + self.break_line2 = line_number(self.main_source, '// Set breakpoint 2') + + + def launchProcess(self): + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + bpt1 = target.BreakpointCreateByLocation(self.main_source, self.break_line1) + self.assertTrue(bpt1, VALID_BREAKPOINT) + bpt2 = target.BreakpointCreateByLocation(self.main_source, self.break_line2) + self.assertTrue(bpt2, VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, bpt1) + + # Make sure we stopped at the first breakpoint. + self.assertTrue (len(thread_list) != 0, "No thread stopped at our breakpoint.") + self.assertTrue (len(thread_list) == 1, "More than one thread stopped at our breakpoint.") + + frame = thread_list[0].GetFrameAtIndex(0) + self.assertTrue (frame, "Got a valid frame 0 frame.") + + def go_interface_types(self): + f = self.frame() + v = f.FindVariable("a", lldb.eDynamicCanRunTarget) + self.assertEqual("*int", v.GetType().name) + self.assertEqual(1, v.Dereference().GetValueAsSigned()) + v = f.FindVariable("b", lldb.eDynamicCanRunTarget) + self.assertEqual("*float64", v.GetType().name) + err = lldb.SBError() + self.assertEqual(2.0, v.Dereference().GetData().GetDouble(err, 0)) + v = f.FindVariable("c", lldb.eDynamicCanRunTarget) + self.assertEqual("*main.SomeFooer", v.GetType().name) + self.assertEqual(9, v.Dereference().GetChildAtIndex(0).GetValueAsSigned()) + v = f.FindVariable("d", lldb.eDynamicCanRunTarget) + self.assertEqual("*main.AnotherFooer", v.GetType().name) + self.assertEqual(-1, v.Dereference().GetChildAtIndex(0).GetValueAsSigned()) + self.assertEqual(-2, v.Dereference().GetChildAtIndex(1).GetValueAsSigned()) + self.assertEqual(-3, v.Dereference().GetChildAtIndex(2).GetValueAsSigned()) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() Index: test/lang/go/runtime/main.go =================================================================== --- /dev/null +++ test/lang/go/runtime/main.go @@ -0,0 +1,38 @@ +package main + +import "fmt" + +type Fooer interface { + Foo() int +} + +type SomeFooer struct { + val int +} + +func (s SomeFooer) Foo() int { + return s.val +} + +type AnotherFooer struct { + a, b, c int +} + +func (s AnotherFooer) Foo() int { + return s.a +} + + +func printEface(a, b, c, d interface{}) { + fmt.Println(a, b, c, d) // Set breakpoint 1 +} + +func printIface(a, b Fooer) { + fmt.Println(a, b) // Set breakpoint 2 +} +func main() { + sf := SomeFooer{9} + af := AnotherFooer{-1, -2, -3} + printEface(1,2.0, sf, af) + printIface(sf, af) +} \ No newline at end of file