[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.

llvm-svn: 251293
This commit is contained in:
Ewan Crawford 2015-10-26 14:04:37 +00:00
parent cb68efea3b
commit 018f5a7e0b
2 changed files with 263 additions and 22 deletions

View File

@ -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::CreateKernelBreakpoint(const ConstString& name)
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::AttemptBreakpointAtKernelName(Stream &strm, const char* nam
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 CommandObjectRenderScriptRuntimeKernelBreakpointSet : public CommandObject
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)
{
}
virtual Options*
GetOptions()
{
return &m_options;
}
class CommandOptions : public Options
{
public:
CommandOptions(CommandInterpreter &interpreter) : Options(interpreter)
{
}
virtual
~CommandOptions()
{
}
virtual Error
SetOptionValue(uint32_t option_idx, const char *option_arg)
{
Error error;
const int short_option = m_getopt_table[option_idx].val;
switch (short_option)
{
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, &regex_match))
matched = true;
else if(regex.Compile("^([0-9]+),([0-9]+)$") && regex.Execute(id_cstr, &regex_match))
matched = true;
else if(regex.Compile("^([0-9]+)$") && regex.Execute(id_cstr, &regex_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)
if (argc < 1)
{
RenderScriptRuntime *runtime =
(RenderScriptRuntime *)m_exe_ctx.GetProcessPtr()->GetLanguageRuntime(eLanguageTypeExtRenderScript);
Error error;
runtime->AttemptBreakpointAtKernelName(result.GetOutputStream(), command.GetArgumentAtIndex(0),
error, m_exe_ctx.GetTargetSP());
if (error.Success())
{
result.AppendMessage("Breakpoint(s) created");
result.SetStatus(eReturnStatusSuccessFinishResult);
return true;
}
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:

View File

@ -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_private::CPPLanguageRuntime
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_private::CPPLanguageRuntime
void Update();
void Initiate();
protected:
struct ScriptDetails;
@ -281,6 +283,7 @@ class RenderScriptRuntime : public lldb_private::CPPLanguageRuntime
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_private::CPPLanguageRuntime
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_private::CPPLanguageRuntime
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