Index: source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h =================================================================== --- source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h +++ source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h @@ -147,8 +147,12 @@ void DumpKernels(Stream &strm) const; + void DumpBreakpoints(Stream &strm) const; + void AttemptBreakpointAtKernelName(Stream &strm, const char *name, Error &error); + void DeleteBreakpoint(Stream &strm, const char* name); + void Status(Stream &strm) const; virtual size_t GetAlternateManglings(const ConstString &mangled, std::vector &alternates) { @@ -206,6 +210,8 @@ std::map m_scriptMappings; std::map m_runtimeHooks; + std::map m_kernel_breakpoints; // BreakpointSP is null if breakpoint is pending + bool m_initiated; bool m_debuggerPresentFlagged; static const HookDefn s_runtimeHookDefns[]; @@ -225,6 +231,9 @@ void CaptureAllocationInit1(RuntimeHook* hook_info, ExecutionContext& context); void CaptureSetGlobalVar1(RuntimeHook* hook_info, ExecutionContext& context); + void CheckModuleForPendingBreakpoints(const RSModuleDescriptorSP& module_desc); + bool ResolveKernelBreakpoint(ConstString kernel_name, const RSModuleDescriptorSP& module, + Error& error, lldb::BreakpointSP& bp, Stream* strm = nullptr); }; } // namespace lldb_private Index: source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp =================================================================== --- source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp +++ source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp @@ -28,6 +28,8 @@ #include "lldb/Symbol/VariableList.h" +#include + using namespace lldb; using namespace lldb_private; @@ -496,7 +498,12 @@ for (const auto &rs_module : m_rsmodules) { if (rs_module->m_module == module_sp) + { + // The dynamic loader can update the loaded sections + // after we have already seen the module. + CheckModuleForPendingBreakpoints(rs_module); return false; + } } bool module_loaded = false; switch (GetModuleKind(module_sp)) @@ -507,6 +514,7 @@ module_desc.reset(new RSModuleDescriptor(module_sp)); if (module_desc->ParseRSInfo()) { + CheckModuleForPendingBreakpoints(module_desc); m_rsmodules.push_back(module_desc); module_loaded = true; } @@ -767,72 +775,189 @@ strm.IndentLess(); } -void -RenderScriptRuntime::AttemptBreakpointAtKernelName(Stream &strm, const char* name, Error& error) +void +RenderScriptRuntime::DumpBreakpoints(Stream &strm) const { - if (!name) + strm.Printf("RenderScript Kernel Breakpoints:"); + strm.EOL(); + strm.IndentMore(); + for (const auto &bp_pair : m_kernel_breakpoints) { - error.SetErrorString("invalid kernel name"); - return; + strm.Printf("\t%s - ", bp_pair.first.AsCString()); + if (bp_pair.second == nullptr) + strm.Printf("Pending on RS module load"); + else + bp_pair.second->GetDescription(&strm, lldb::eDescriptionLevelBrief, false); + + strm.EOL(); } + strm.IndentLess(); +} - bool kernels_found = false; +void +RenderScriptRuntime::DeleteBreakpoint(Stream &strm, const char* name) +{ ConstString kernel_name(name); - for (const auto &module : m_rsmodules) + auto match = m_kernel_breakpoints.find(kernel_name); + + if (match != m_kernel_breakpoints.end()) { - for (const auto &kernel : module->m_kernels) + BreakpointSP bp = match->second; + if (bp != nullptr) + GetProcess()->GetTarget().RemoveBreakpointByID(bp->GetID()); + strm.Printf("Breakpoint deleted"); + m_kernel_breakpoints.erase(match); + } + else + strm.Printf("No kernel breakpoint found matching '%s'", name); + + strm.EOL(); +} + +// Given the name of a kernel and a RS module, check to see if there is a matching kernel in the module. +// If so a breakpoint is set on the kernel and true is returned, otherwise return false. +bool +RenderScriptRuntime::ResolveKernelBreakpoint(ConstString kernel_name, + const RSModuleDescriptorSP& module, + Error& error, + BreakpointSP& bp, + Stream* strm) +{ + Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + + for (const auto &kernel : module->m_kernels) + { + if (kernel.m_name == kernel_name) { - if (kernel.m_name == kernel_name) + // Attempt to set a breakpoint on this symbol, within the module library + // If it's not found, it's likely debug info is unavailable - set a + // breakpoint on .expand and emit a warning. + + const Symbol* kernel_sym = module->m_module->FindFirstSymbolWithNameAndType(kernel_name, eSymbolTypeCode); + const char* kernel_name_cstr = kernel_name.AsCString(); + + if (!kernel_sym) { - //Attempt to set a breakpoint on this symbol, within the module library - //If it's not found, it's likely debug info is unavailable - set a - //breakpoint on .expand and emit a warning. + std::string kernel_name_expanded(kernel_name_cstr); + kernel_name_expanded.append(".expand"); + kernel_sym = module->m_module->FindFirstSymbolWithNameAndType(ConstString(kernel_name_expanded.c_str()), + eSymbolTypeCode); - const Symbol* kernel_sym = module->m_module->FindFirstSymbolWithNameAndType(kernel_name, eSymbolTypeCode); - - if (!kernel_sym) + if (kernel_sym && strm != nullptr) { - std::string kernel_name_expanded(name); - kernel_name_expanded.append(".expand"); - kernel_sym = module->m_module->FindFirstSymbolWithNameAndType(ConstString(kernel_name_expanded.c_str()), eSymbolTypeCode); - - if (kernel_sym) - { - strm.Printf("Kernel '%s' could not be found, but expansion exists. ", name); - strm.Printf("Breakpoint placed on expanded kernel. Have you compiled in debug mode?"); - strm.EOL(); - } - else - { - error.SetErrorStringWithFormat("Could not locate symbols for loaded kernel '%s'.", name); - return; - } + strm->Printf("Kernel '%s' could not be found, but expansion exists. ", kernel_name_cstr); + strm->Printf("Breakpoint placed on expanded kernel. Have you compiled in debug mode?"); + strm->EOL(); } + } + if (kernel_sym) + { addr_t bp_addr = kernel_sym->GetLoadAddress(&GetProcess()->GetTarget()); if (bp_addr == LLDB_INVALID_ADDRESS) { - error.SetErrorStringWithFormat("Could not locate load address for symbols of kernel '%s'.", name); - return; + error.SetErrorStringWithFormat("Could not locate load address for symbols of kernel '%s'.", + kernel_name_cstr); + return false; } - BreakpointSP bp = GetProcess()->GetTarget().CreateBreakpoint(bp_addr, false, false); - strm.Printf("Breakpoint %" PRIu64 ": kernel '%s' within script '%s'", (uint64_t)bp->GetID(), name, module->m_resname.c_str()); - strm.EOL(); + bp = GetProcess()->GetTarget().CreateBreakpoint(bp_addr, false, false); + if (strm != nullptr) + { + strm->Printf("Breakpoint %" PRIu64 ": kernel '%s' within script '%s'", (uint64_t)bp->GetID(), + kernel_name_cstr, + module->m_resname.c_str()); + strm->EOL(); + } + } + else + { + error.SetErrorStringWithFormat("Could not locate symbols for loaded kernel '%s'.", kernel_name_cstr); + return false; + } - kernels_found = true; - } + if (log) + log->Printf ("RenderScriptRuntime::ResolveKernelBreakpoint - '%s' kernel breakpoint resolved.", + kernel_name_cstr); + + return true; } } + return false; +} - if (!kernels_found) +void +RenderScriptRuntime::AttemptBreakpointAtKernelName(Stream &strm, const char* name, Error& error) +{ + if (!name) { - error.SetErrorString("kernel name not found"); + error.SetErrorString("invalid kernel name"); + return; } + + ConstString kernel_name(name); + + // Only create one pending breakpoint per kernel name + auto end_itr = m_kernel_breakpoints.end(); + auto matching_itr = m_kernel_breakpoints.find(kernel_name); + + if (matching_itr == end_itr) + { + // Check if the kernel breakpoint can be set in any of the loaded RS modules + BreakpointSP bp; + bool kernel_found = std::any_of + ( + m_rsmodules.begin(), + m_rsmodules.end(), + [&](const RSModuleDescriptorSP module) + { + return ResolveKernelBreakpoint(kernel_name, module, error, bp, &strm); + } + ); + + if (!kernel_found) + { + m_kernel_breakpoints[kernel_name] = nullptr; // Pending breakpoint + strm.Printf("Kernel could not be found, pending breakpoint created"); + strm.EOL(); + } + else + m_kernel_breakpoints[kernel_name] = bp; + } + else + error.SetErrorStringWithFormat("Breakpoint already exists for kernel '%s'.", name); + return; } +// Iterate over all the kernel breakpoints which haven't yet been resolved to an address. +// If they can be set in the module, then remove them for the list of pending breakpoints. void +RenderScriptRuntime::CheckModuleForPendingBreakpoints(const RSModuleDescriptorSP& module_desc) +{ + Log* log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + + // Kernels to remove from list which we have now set a breakpoint at + for (auto iter = m_kernel_breakpoints.begin(); iter != m_kernel_breakpoints.end(); ++iter) + { + Error err; + BreakpointSP bp; + + if (iter->second != nullptr) // Breakpoint already set on kernel + continue; + + bool kernel_bp_set = ResolveKernelBreakpoint(iter->first, module_desc, err, bp); + + if (kernel_bp_set) + iter->second = bp; + + if (log && !err.Success()) + log->Printf ("RenderScriptRuntime::CheckModuleForPendingBreakpoints - Error '%s'", + err.AsCString()); + } +} + +void RenderScriptRuntime::DumpModules(Stream &strm) const { strm.Printf("RenderScript Modules:"); @@ -1031,18 +1156,18 @@ } }; -class CommandObjectRenderScriptRuntimeKernelBreakpoint : public CommandObjectParsed +class CommandObjectRenderScriptRuntimeKernelBreakpointSet : public CommandObjectParsed { private: public: - CommandObjectRenderScriptRuntimeKernelBreakpoint(CommandInterpreter &interpreter) - : CommandObjectParsed(interpreter, "renderscript kernel breakpoint", - "Sets a breakpoint on a renderscript kernel.", "renderscript kernel breakpoint", + CommandObjectRenderScriptRuntimeKernelBreakpointSet(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "renderscript kernel breakpoint set", + "Set a breakpoint on a renderscript kernel.", "renderscript kernel breakpoint set ", eCommandRequiresProcess | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) { } - ~CommandObjectRenderScriptRuntimeKernelBreakpoint() {} + ~CommandObjectRenderScriptRuntimeKernelBreakpointSet() {} bool DoExecute(Args &command, CommandReturnObject &result) @@ -1051,7 +1176,7 @@ if (argc == 1) { RenderScriptRuntime *runtime = - (RenderScriptRuntime *)m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript); + static_cast(m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript)); Error error; runtime->AttemptBreakpointAtKernelName(result.GetOutputStream(), command.GetArgumentAtIndex(0), error); @@ -1073,6 +1198,84 @@ } }; +class CommandObjectRenderScriptRuntimeKernelBreakpointList : public CommandObjectParsed +{ + private: + public: + CommandObjectRenderScriptRuntimeKernelBreakpointList(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "renderscript kernel breakpoint list", + "List all renderscript kernel breakpoints.", "renderscript kernel breakpoint list", + eCommandRequiresProcess | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) + { + } + + ~CommandObjectRenderScriptRuntimeKernelBreakpointList() {} + + bool + DoExecute(Args &command, CommandReturnObject &result) + { + RenderScriptRuntime *runtime = + static_cast(m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript)); + + runtime->DumpBreakpoints(result.GetOutputStream()); + + result.SetStatus(eReturnStatusSuccessFinishResult); + return true; + } +}; + +class CommandObjectRenderScriptRuntimeKernelBreakpointDelete : public CommandObjectParsed +{ + private: + public: + CommandObjectRenderScriptRuntimeKernelBreakpointDelete(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "renderscript kernel breakpoint delete", + "Remove renderscript kernel breakpoint.", "renderscript kernel breakpoint delete ", + eCommandRequiresProcess | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) + { + } + + ~CommandObjectRenderScriptRuntimeKernelBreakpointDelete() {} + + bool + DoExecute(Args &command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + if (argc == 1) + { + RenderScriptRuntime *runtime = + static_cast(m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript)); + + runtime->DeleteBreakpoint(result.GetOutputStream(), command.GetArgumentAtIndex(0)); + + result.SetStatus(eReturnStatusSuccessFinishResult); + return true; + } + + result.AppendErrorWithFormat("'%s' takes 1 argument of kernel name", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } +}; + +class CommandObjectRenderScriptRuntimeKernelBreakpoint : public CommandObjectMultiword +{ + private: + public: + CommandObjectRenderScriptRuntimeKernelBreakpoint(CommandInterpreter &interpreter) + : CommandObjectMultiword(interpreter, "renderscript kernel breakpoint", + "Commands for managing renderscript kernel breakpoints.", + NULL) + { + LoadSubCommand("list", CommandObjectSP(new CommandObjectRenderScriptRuntimeKernelBreakpointList(interpreter))); + LoadSubCommand("set", CommandObjectSP(new CommandObjectRenderScriptRuntimeKernelBreakpointSet(interpreter))); + LoadSubCommand("delete", CommandObjectSP(new CommandObjectRenderScriptRuntimeKernelBreakpointDelete(interpreter))); + } + + ~CommandObjectRenderScriptRuntimeKernelBreakpoint() {} +}; + + class CommandObjectRenderScriptRuntimeKernel : public CommandObjectMultiword { private: