3 #include "chi/Debugger/Debugger.hpp" 11 #include <boost/filesystem.hpp> 12 #include <boost/uuid/uuid_io.hpp> 14 #if LLVM_VERSION_LESS_EQUAL(3, 9) 15 #include <llvm/Bitcode/ReaderWriter.h> 17 #include <llvm/Bitcode/BitcodeWriter.h> 20 #include <llvm/IR/Module.h> 21 #include <llvm/Support/FileSystem.h> 22 #include <llvm/Support/raw_ostream.h> 24 #include <lldb/API/SBListener.h> 25 #include <lldb/API/SBThread.h> 29 namespace fs = boost::filesystem;
36 auto lldbServerPath = fs::path(pathToChig).parent_path() /
"lldb-server";
37 setenv(
"LLDB_DEBUGSERVER_PATH", lldbServerPath.c_str(), 1);
40 lldb::SBDebugger::Initialize();
41 mDebugger = lldb::SBDebugger::Create();
44 mDebugger.SetLoggingCallback(
45 [](
const char* msg,
void* dbg) {
51 const char* val[] = {
"api",
nullptr};
52 mDebugger.EnableLog(
"lldb", val);
55 mTarget = mDebugger.CreateTarget(pathToChig);
64 auto err = mProcess.Kill();
67 res.
addEntry(
"EUKN",
"Failed to terminate process",
68 {{
"Error Code", err.GetError()}, {
"Error String", err.GetCString()}});
81 auto err = mProcess.Continue();
84 res.
addEntry(
"EUKN",
"Failed to continue process",
85 {{
"Error Code", err.GetError()}, {
"Error String", err.GetCString()}});
97 auto err = mProcess.Stop();
100 res.
addEntry(
"EUKN",
"Failed to pause process",
101 {{
"Error Code", err.GetError()}, {
"Error String", err.GetCString()}});
116 auto breakpoint = mTarget.BreakpointCreateByLocation(
120 if (!breakpoint.IsValid()) {
121 res.
addEntry(
"EUKN",
"Could not set breakpoint on node",
124 {
"Line Number", linenum}});
129 mBreakpoints[&node] = breakpoint;
131 if (bp !=
nullptr) { *bp = breakpoint; }
133 breakpoint.SetEnabled(
true);
139 auto iter = mBreakpoints.find(&node);
140 if (iter == mBreakpoints.end()) {
return false; }
142 return mTarget.BreakpointDelete(iter->second.GetID());
146 const boost::filesystem::path& workingDirectory) {
147 assert(boost::filesystem::is_directory(workingDirectory));
151 if (!mTarget.IsValid()) {
152 res.
addEntry(
"EUKN",
"Cannot start a debugger process with an invalid target", {});
157 std::unique_ptr<llvm::Module> mod;
160 if (!res) {
return res; }
166 tmpIRPath = boost::filesystem::temp_directory_path() / fs::unique_path();
168 std::string errorString;
169 llvm::raw_fd_ostream file {
170 tmpIRPath.string().c_str(),
171 #if LLVM_VERSION_LESS_EQUAL(3, 5) 178 llvm::WriteBitcodeToFile(mod.get(), file);
182 std::vector<const char*> args;
183 std::string tmpIRString = tmpIRPath.string();
185 args.push_back(
"interpret");
187 args.push_back(
"-i");
188 args.push_back(tmpIRString.c_str());
190 args.push_back(
"-O");
193 if (argv !=
nullptr) {
194 for (; *argv !=
nullptr; ++argv) { args.push_back(*argv); }
197 args.push_back(
nullptr);
203 lldb::SBListener invalidListener;
205 mTarget.Launch(invalidListener, args.data(), envp,
nullptr,
nullptr,
nullptr,
206 workingDirectory.string().c_str(), lldb::eLaunchFlagDebug,
false, err);
209 res.
addEntry(
"EUKN",
"Failed to launch process", {{
"Error Message", err.GetCString()}});
217 std::vector<const NodeInstance*> ret;
218 ret.reserve(mBreakpoints.size());
219 for (
const auto& bpts : mBreakpoints) { ret.push_back(bpts.first); }
224 lldb::SBFrame frame) {
228 if (!frame.IsValid()) {
230 if (!thread.IsValid()) {
return {}; }
231 frame = thread.GetSelectedFrame();
234 if (!frame.IsValid()) {
return {}; }
238 if (func != &inst.
function()) {
return {}; }
240 auto variableName = inst.
stringId() +
"__" + std::to_string(
id);
241 return frame.FindVariable(variableName.c_str());
245 using namespace std::string_literals;
248 if (!frame.IsValid()) {
250 if (!thread.IsValid()) {
return {}; }
251 frame = thread.GetSelectedFrame();
254 if (!frame.IsValid()) {
return {}; }
256 auto mangledFunctionName = frame.GetFunctionName();
258 std::string moduleName, functionName;
265 if (func ==
nullptr) {
267 if (!mod) {
return nullptr; }
268 func = mod->functionFromName(functionName);
271 unsigned lineNo = frame.GetLineEntry().GetLine();
276 auto nodeIter = assoc.left.find(lineNo);
277 if (nodeIter == assoc.left.end()) {
return nullptr; }
279 return nodeIter->second;
285 auto lineNumberIter = lineAssoc.right.find(&inst);
286 if (lineNumberIter == lineAssoc.right.end()) {
return -1; }
288 return lineNumberIter->second;
Result start(const char **argv=nullptr, const char **envp=nullptr, const boost::filesystem::path &workingDirectory=boost::filesystem::current_path())
Start debugging the process.
bool isAttached() const
Check if this debugger is attached to a process It's true if the process is stopped or if it's curret...
GraphModule & module() const
Get the module that's being debugged by this debugger.
Result processContinue()
Continue the execution of the process.
Result setBreakpoint(NodeInstance &node, lldb::SBBreakpoint *bp=nullptr)
Set a breakpoint on a given node.
ChiModule * moduleByFullName(const boost::filesystem::path &fullModuleName) const noexcept
Gets the module by the full name.
this is an AST-like representation of a function in a graph It is used for IDE-like behavior...
void addEntry(const char *ec, const char *overview, nlohmann::json data)
Add a entry to the result, either a warning or an error.
std::string stringId() const
Get the ID as a string.
Result pause()
Pause the execution state.
Result terminate()
Terminate the inferior process.
boost::bimap< unsigned, NodeInstance * > createLineNumberAssoc() const
Create the associations from line number and function in debug info.
Defines the Result class and related functions.
GraphFunction * functionFromName(boost::string_view name) const
Get a function from the name.
std::vector< std::vector< std::pair< NodeInstance *, size_t > > > outputDataConnections
The connections that lead out of this node, data.
lldb::SBProcess lldbProcess() const
Get the process This will only be valid if there's an attached process.
GraphModule & module() const
Get the containing GraphModule.
Definitions for mangling functions.
GraphModule & module() const
Get the GraphModule that contains this GraphFunction.
lldb::SBValue inspectNodeOutput(const NodeInstance &inst, size_t id, lldb::SBFrame frame={})
Get the output of a node.
unsigned lineNumberFromNode(NodeInstance &inst)
Get the mapped line number from a node.
NodeInstance * nodeFromFrame(lldb::SBFrame frame={})
Get a NodeInstance from a frame.
boost::filesystem::path sourceFilePath() const
Get the path to the source file It's not garunteed to exist, because it could have not been saved...
std::vector< const NodeInstance * > listBreakpoints() const
List the curretnly set breakpoints.
std::pair< std::string, std::string > unmangleFunctionName(std::string mangled)
Unmangle a function name.
GraphFunction & function() const
Get the containing GraphFunction.
Debugger(const char *pathToChig, GraphModule &mod)
Default constructor.
bool removeBreakpoint(NodeInstance &node)
Remove a breakpoint from a node.
Result compileModule(const boost::filesystem::path &fullName, Flags< CompileSettings > settings, std::unique_ptr< llvm::Module > *toFill)
Compile a module to a llvm::Module.
Module that holds graph functions.
The namespace where chigraph lives.
Context & context() const
Get the Context that this module belongs to.
Defines the Context class and related functions.
The result object, used for identifiying errors with good diagnostics.
Defines the NodeInstance class and related functions.