forked from OSchip/llvm-project
[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:
parent
cb68efea3b
commit
018f5a7e0b
|
@ -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, ®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)
|
||||
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:
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue