Author: ewancrawford Date: Mon Oct 26 09:04:37 2015 New Revision: 251293 URL: http://llvm.org/viewvc/llvm-project?rev=251293&view=rev Log: [RenderScript] Add option to break on a specific kernel invocation
Adds option -c <x,y,z> to the 'language renderscript kernel breakpoint set' command. Breaks only on the invocation of the kernel with specified coordinate. Implemented by adding a callback to the kernel breakpoint which checks the coordinates of every invocation. Modified: lldb/trunk/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp lldb/trunk/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h Modified: lldb/trunk/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp?rev=251293&r1=251292&r2=251293&view=diff ============================================================================== --- lldb/trunk/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp (original) +++ lldb/trunk/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.cpp Mon Oct 26 09:04:37 2015 @@ -14,11 +14,13 @@ #include "lldb/Core/Error.h" #include "lldb/Core/Log.h" #include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegularExpression.h" #include "lldb/Host/StringConvert.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/Type.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" #include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/Options.h" #include "lldb/Interpreter/CommandInterpreter.h" @@ -2301,8 +2303,124 @@ RenderScriptRuntime::CreateKernelBreakpo return bp; } +// Given an expression for a variable this function tries to calculate the variable's value. +// If this is possible it returns true and sets the uint64_t parameter to the variables unsigned value. +// Otherwise function returns false. +bool +RenderScriptRuntime::GetFrameVarAsUnsigned(const StackFrameSP frame_sp, const char* var_name, uint64_t& val) +{ + Log* log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE)); + Error error; + VariableSP var_sp; + + // Find variable in stack frame + ValueObjectSP value_sp(frame_sp->GetValueForVariableExpressionPath(var_name, + eNoDynamicValues, + StackFrame::eExpressionPathOptionCheckPtrVsMember | + StackFrame::eExpressionPathOptionsAllowDirectIVarAccess, + var_sp, + error)); + if (!error.Success()) + { + if (log) + log->Printf("RenderScriptRuntime::GetFrameVarAsUnsigned - Error, couldn't find '%s' in frame", var_name); + + return false; + } + + // Find the unsigned int value for the variable + bool success = false; + val = value_sp->GetValueAsUnsigned(0, &success); + if (!success) + { + if (log) + log->Printf("RenderScriptRuntime::GetFrameVarAsUnsigned - Error, couldn't parse '%s' as an unsigned int", var_name); + + return false; + } + + return true; +} + +// Callback when a kernel breakpoint hits and we're looking for a specific coordinate. +// Baton parameter contains a pointer to the target coordinate we want to break on. +// Function then checks the .expand frame for the current coordinate and breaks to user if it matches. +// Parameter 'break_id' is the id of the Breakpoint which made the callback. +// Parameter 'break_loc_id' is the id for the BreakpointLocation which was hit, +// a single logical breakpoint can have multiple addresses. +bool +RenderScriptRuntime::KernelBreakpointHit(void *baton, StoppointCallbackContext *ctx, + user_id_t break_id, user_id_t break_loc_id) +{ + Log* log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_BREAKPOINTS)); + + assert(baton && "Error: null baton in conditional kernel breakpoint callback"); + + // Coordinate we want to stop on + const int* target_coord = static_cast<const int*>(baton); + + if (log) + log->Printf("RenderScriptRuntime::KernelBreakpointHit - Break ID %" PRIu64 ", target coord (%d, %d, %d)", + break_id, target_coord[0], target_coord[1], target_coord[2]); + + // Go up one stack frame to .expand kernel + ExecutionContext context(ctx->exe_ctx_ref); + ThreadSP thread_sp = context.GetThreadSP(); + if (!thread_sp->SetSelectedFrameByIndex(1)) + { + if (log) + log->Printf("RenderScriptRuntime::KernelBreakpointHit - Error, couldn't go up stack frame"); + + return false; + } + + StackFrameSP frame_sp = thread_sp->GetSelectedFrame(); + if (!frame_sp) + { + if (log) + log->Printf("RenderScriptRuntime::KernelBreakpointHit - Error, couldn't select .expand stack frame"); + + return false; + } + + // Get values for variables in .expand frame that tell us the current kernel invocation + const char* coord_expressions[] = {"rsIndex", "p->current.y", "p->current.z"}; + uint64_t current_coord[3] = {0, 0, 0}; + + for(int i = 0; i < 3; ++i) + { + if (!GetFrameVarAsUnsigned(frame_sp, coord_expressions[i], current_coord[i])) + return false; + + if (log) + log->Printf("RenderScriptRuntime::KernelBreakpointHit, %s = %" PRIu64, coord_expressions[i], current_coord[i]); + } + + // Check if the current kernel invocation coordinate matches our target coordinate + if (current_coord[0] == static_cast<uint64_t>(target_coord[0]) && + current_coord[1] == static_cast<uint64_t>(target_coord[1]) && + current_coord[2] == static_cast<uint64_t>(target_coord[2])) + { + if (log) + log->Printf("RenderScriptRuntime::KernelBreakpointHit, BREAKING %" PRIu64 ", %" PRIu64 ", %" PRIu64, + current_coord[0], current_coord[1], current_coord[2]); + + BreakpointSP breakpoint_sp = context.GetTargetPtr()->GetBreakpointByID(break_id); + assert(breakpoint_sp != nullptr && "Error: Couldn't find breakpoint matching break id for callback"); + breakpoint_sp->SetEnabled(false); // Optimise since conditional breakpoint should only be hit once. + return true; + } + + // No match on coordinate + return false; +} + +// Tries to set a breakpoint on the start of a kernel, resolved using the kernel name. +// Argument 'coords', represents a three dimensional coordinate which can be used to specify +// a single kernel instance to break on. If this is set then we add a callback to the breakpoint. void -RenderScriptRuntime::AttemptBreakpointAtKernelName(Stream &strm, const char* name, Error& error, TargetSP target) +RenderScriptRuntime::PlaceBreakpointOnKernel(Stream &strm, const char* name, const std::array<int,3> coords, + Error& error, TargetSP target) { if (!name) { @@ -2314,6 +2432,25 @@ RenderScriptRuntime::AttemptBreakpointAt ConstString kernel_name(name); BreakpointSP bp = CreateKernelBreakpoint(kernel_name); + + // We have a conditional breakpoint on a specific coordinate + if (coords[0] != -1) + { + strm.Printf("Conditional kernel breakpoint on coordinate %d, %d, %d", coords[0], coords[1], coords[2]); + strm.EOL(); + + // Allocate memory for the baton, and copy over coordinate + int* baton = new int[3]; + baton[0] = coords[0]; baton[1] = coords[1]; baton[2] = coords[2]; + + // Create a callback that will be invoked everytime the breakpoint is hit. + // The baton object passed to the handler is the target coordinate we want to break on. + bp->SetCallback(KernelBreakpointHit, baton, true); + + // Store a shared pointer to the baton, so the memory will eventually be cleaned up after destruction + m_conditional_breaks[bp->GetID()] = std::shared_ptr<int>(baton); + } + if (bp) bp->GetDescription(&strm, lldb::eDescriptionLevelInitial, false); @@ -2562,43 +2699,140 @@ class CommandObjectRenderScriptRuntimeKe public: CommandObjectRenderScriptRuntimeKernelBreakpointSet(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "renderscript kernel breakpoint set", - "Sets a breakpoint on a renderscript kernel.", "renderscript kernel breakpoint set <kernel_name>", - eCommandRequiresProcess | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) + "Sets a breakpoint on a renderscript kernel.", "renderscript kernel breakpoint set <kernel_name> [-c x,y,z]", + eCommandRequiresProcess | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_options(interpreter) { } - ~CommandObjectRenderScriptRuntimeKernelBreakpointSet() {} + virtual Options* + GetOptions() + { + return &m_options; + } - bool - DoExecute(Args &command, CommandReturnObject &result) + class CommandOptions : public Options { - const size_t argc = command.GetArgumentCount(); - if (argc == 1) + public: + CommandOptions(CommandInterpreter &interpreter) : Options(interpreter) { - RenderScriptRuntime *runtime = - (RenderScriptRuntime *)m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript); + } + + virtual + ~CommandOptions() + { + } + virtual Error + SetOptionValue(uint32_t option_idx, const char *option_arg) + { Error error; - runtime->AttemptBreakpointAtKernelName(result.GetOutputStream(), command.GetArgumentAtIndex(0), - error, m_exe_ctx.GetTargetSP()); + const int short_option = m_getopt_table[option_idx].val; - if (error.Success()) + switch (short_option) { - result.AppendMessage("Breakpoint(s) created"); - result.SetStatus(eReturnStatusSuccessFinishResult); - return true; + case 'c': + if (!ParseCoordinate(option_arg)) + error.SetErrorStringWithFormat("Couldn't parse coordinate '%s', should be in format 'x,y,z'.", option_arg); + break; + default: + error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); + break; + } + return error; + } + + // -c takes an argument of the form 'num[,num][,num]'. + // Where 'id_cstr' is this argument with the whitespace trimmed. + // Missing coordinates are defaulted to zero. + bool + ParseCoordinate(const char* id_cstr) + { + RegularExpression regex; + RegularExpression::Match regex_match(3); + + bool matched = false; + if(regex.Compile("^([0-9]+),([0-9]+),([0-9]+)$") && regex.Execute(id_cstr, ®ex_match)) + matched = true; + else if(regex.Compile("^([0-9]+),([0-9]+)$") && regex.Execute(id_cstr, ®ex_match)) + matched = true; + else if(regex.Compile("^([0-9]+)$") && regex.Execute(id_cstr, ®ex_match)) + matched = true; + for(uint32_t i = 0; i < 3; i++) + { + std::string group; + if(regex_match.GetMatchAtIndex(id_cstr, i + 1, group)) + m_coord[i] = (uint32_t)strtoul(group.c_str(), NULL, 0); + else + m_coord[i] = 0; } + return matched; + } + + void + OptionParsingStarting() + { + // -1 means the -c option hasn't been set + m_coord[0] = -1; + m_coord[1] = -1; + m_coord[2] = -1; + } + + const OptionDefinition* + GetDefinitions() + { + return g_option_table; + } + + static OptionDefinition g_option_table[]; + std::array<int,3> m_coord; + }; + + ~CommandObjectRenderScriptRuntimeKernelBreakpointSet() {} + + bool + DoExecute(Args &command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + if (argc < 1) + { + result.AppendErrorWithFormat("'%s' takes 1 argument of kernel name, and an optional coordinate.", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); - result.AppendErrorWithFormat("Error: %s", error.AsCString()); return false; } - result.AppendErrorWithFormat("'%s' takes 1 argument of kernel name", m_cmd_name.c_str()); + RenderScriptRuntime *runtime = + (RenderScriptRuntime *)m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript); + + Error error; + runtime->PlaceBreakpointOnKernel(result.GetOutputStream(), command.GetArgumentAtIndex(0), m_options.m_coord, + error, m_exe_ctx.GetTargetSP()); + + if (error.Success()) + { + result.AppendMessage("Breakpoint(s) created"); + result.SetStatus(eReturnStatusSuccessFinishResult); + return true; + } result.SetStatus(eReturnStatusFailed); + result.AppendErrorWithFormat("Error: %s", error.AsCString()); return false; - } + } + + private: + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectRenderScriptRuntimeKernelBreakpointSet::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "coordinate", 'c', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeValue, + "Set a breakpoint on a single invocation of the kernel with specified coordinate.\n" + "Coordinate takes the form 'x[,y][,z] where x,y,z are positive integers representing kernel dimensions. " + "Any unset dimensions will be defaulted to zero."}, + { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; + class CommandObjectRenderScriptRuntimeKernelBreakpointAll : public CommandObjectParsed { private: Modified: lldb/trunk/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h?rev=251293&r1=251292&r2=251293&view=diff ============================================================================== --- lldb/trunk/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h (original) +++ lldb/trunk/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h Mon Oct 26 09:04:37 2015 @@ -19,6 +19,7 @@ #include "lldb/Target/CPPLanguageRuntime.h" #include "lldb/Core/Module.h" +#include <array> namespace lldb_private { @@ -206,7 +207,8 @@ class RenderScriptRuntime : public lldb_ void ListAllocations(Stream &strm, StackFrame* frame_ptr, bool recompute); - void AttemptBreakpointAtKernelName(Stream &strm, const char *name, Error &error, lldb::TargetSP target); + void PlaceBreakpointOnKernel(Stream &strm, const char *name, const std::array<int,3> coords, + Error &error, lldb::TargetSP target); void SetBreakAllKernels(bool do_break, lldb::TargetSP target); @@ -225,7 +227,7 @@ class RenderScriptRuntime : public lldb_ void Update(); void Initiate(); - + protected: struct ScriptDetails; @@ -281,6 +283,7 @@ class RenderScriptRuntime : public lldb_ std::map<lldb::addr_t, lldb_renderscript::RSModuleDescriptorSP> m_scriptMappings; std::map<lldb::addr_t, RuntimeHookSP> m_runtimeHooks; + std::map<lldb::user_id_t, std::shared_ptr<int>> m_conditional_breaks; lldb::SearchFilterSP m_filtersp; // Needed to create breakpoints through Target API @@ -296,6 +299,9 @@ class RenderScriptRuntime : public lldb_ static bool HookCallback(void *baton, StoppointCallbackContext *ctx, lldb::user_id_t break_id, lldb::user_id_t break_loc_id); + static bool KernelBreakpointHit(void *baton, StoppointCallbackContext *ctx, + lldb::user_id_t break_id, lldb::user_id_t break_loc_id); + void HookCallback(RuntimeHook* hook_info, ExecutionContext& context); bool GetArgSimple(ExecutionContext& context, uint32_t arg, uint64_t* data); @@ -307,6 +313,7 @@ class RenderScriptRuntime : public lldb_ AllocationDetails* FindAllocByID(Stream &strm, const uint32_t alloc_id); std::shared_ptr<uint8_t> GetAllocationData(AllocationDetails* allocation, StackFrame* frame_ptr); unsigned int GetElementSize(const AllocationDetails* allocation); + static bool GetFrameVarAsUnsigned(const lldb::StackFrameSP, const char* var_name, uint64_t& val); // // Helper functions for jitting the runtime _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits