This commit fixes an issue with lldb-vscode failing to run programs that
use different architecture/platform than the "empty" in the target.
Original implementation was creating a default target without specifying
the target architecture, platform or program, and then would set
executable file through SBLaunchInfo, assuming that this would update
architecture and platform accordingly. However this wasn't really
happening, and architecture and platform would remain at whatever values
were in the "empty" target. The simple solution is to create target
already for a desired architecture and platform.
Function request_attach is updated in a similar fashion.
This commit also adds new JSON properties to "launch" and "attach"
packets to allow user to override desired platform and architecture.
This might be especially important for cases where information in ELF is
not enough to derive those values correctly.
New code has a behavior similar to LLDB MI [1], where typically IDE would
specify target file with -file-exec-and-symbols, and then only do -exec-run
command that would launch the process. In lldb-vscode those two actions are
merged into one request_launch function. Similarly in the interpreter
session, user would first do "file" command, then "process launch"
You will lose any state that was set in the original target by "g_vsc.RunInitCommands();" if you do this. Not sure if this matters since users can use "RunPreRunCommands"...
Also the original g_vsc._target had tried to listen to events from this target. From the "initialize" code:
void request_initialize(const llvm::json::Object &request) { g_vsc.debugger = lldb::SBDebugger::Create(true /*source_init_files*/); // Create an empty target right away since we might get breakpoint requests // before we are given an executable to launch in a "launch" request, or a // executable when attaching to a process by process ID in a "attach" // request. ... g_vsc.target = g_vsc.debugger.CreateTarget(nullptr); lldb::SBListener listener = g_vsc.debugger.GetListener(); listener.StartListeningForEvents( g_vsc.target.GetBroadcaster(), lldb::SBTarget::eBroadcastBitBreakpointChanged); listener.StartListeningForEvents(g_vsc.broadcaster, eBroadcastBitStopEventThread);Though if this is patch works for you this must not be needed?
Do breakpoints work fine for you with this patch when you set them before you run? This comment might be out of date as I think I fixed when I sent the packet:
{"event":"initialized","seq":0,"type":"event"}Until after the process was launched.
Where things seems to go wrong is in "Target::Launch(...)":
// If we're not already connected to the process, and if we have a platform // that can launch a process for debugging, go ahead and do that here. if (state != eStateConnected && platform_sp && platform_sp->CanDebugProcess()) {The platform could be incorrect if the executable inside the launch info doesn't match the platform, or even if it works for the platform, it might not be the platform that exists.
A better fix might be to modify Target::GetOrCreateModule(...) (which is called by SBTarget::AddModule()) as follows:
if (module_sp) { bool isExecutable = false; // NEW CODE ObjectFile *objfile = module_sp->GetObjectFile(); if (objfile) { switch (objfile->GetType()) { case ObjectFile::eTypeExecutable: /// A normal executable isExecutable = true; // NEW CODE (and moved) LLVM_FALLTHROUGH; // NEW CODE case ObjectFile::eTypeCoreFile: /// A core file that has a checkpoint of /// a program's execution state case ObjectFile::eTypeDynamicLinker: /// The platform's dynamic linker /// executable case ObjectFile::eTypeObjectFile: /// An intermediate object file case ObjectFile::eTypeSharedLibrary: /// A shared library that can be /// used during execution break; case ObjectFile::eTypeDebugInfo: /// An object file that contains only /// debug information if (error_ptr) error_ptr->SetErrorString("debug info files aren't valid target " "modules, please specify an executable"); return ModuleSP(); case ObjectFile::eTypeStubLibrary: /// A library that can be linked /// against but not used for /// execution if (error_ptr) error_ptr->SetErrorString("stub libraries aren't valid target " "modules, please specify an executable"); return ModuleSP(); default: if (error_ptr) error_ptr->SetErrorString( "unsupported file type, please specify an executable"); return ModuleSP(); } // GetSharedModule is not guaranteed to find the old shared module, for // instance in the common case where you pass in the UUID, it is only // going to find the one module matching the UUID. In fact, it has no // good way to know what the "old module" relevant to this target is, // since there might be many copies of a module with this file spec in // various running debug sessions, but only one of them will belong to // this target. So let's remove the UUID from the module list, and look // in the target's module list. Only do this if there is SOMETHING else // in the module spec... if (!old_module_sp) { if (module_spec.GetUUID().IsValid() && !module_spec.GetFileSpec().GetFilename().IsEmpty() && !module_spec.GetFileSpec().GetDirectory().IsEmpty()) { ModuleSpec module_spec_copy(module_spec.GetFileSpec()); module_spec_copy.GetUUID().Clear(); ModuleList found_modules; size_t num_found = m_images.FindModules(module_spec_copy, found_modules); if (num_found == 1) { old_module_sp = found_modules.GetModuleAtIndex(0); } } } // Preload symbols outside of any lock, so hopefully we can do this for // each library in parallel. if (GetPreloadSymbols()) module_sp->PreloadSymbols(); if (old_module_sp && m_images.GetIndexForModule(old_module_sp.get()) != LLDB_INVALID_INDEX32) { m_images.ReplaceModule(old_module_sp, module_sp); Module *old_module_ptr = old_module_sp.get(); old_module_sp.reset(); ModuleList::RemoveSharedModuleIfOrphaned(old_module_ptr); } else { m_images.Append(module_sp, notify); } if (isExecutable) // NEW CODE SetExecutableModule(module_sp, false); // NEW CODE } else module_sp.reset(); }Look for "// NEW CODE" in the above snippet.