[Lldb-commits] [lldb] r251293 - [RenderScript] Add option to break on a specific kernel invocation

Ewan Crawford via lldb-commits lldb-commits at lists.llvm.org
Mon Oct 26 07:04:37 PDT 2015


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, &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)
+        {
+            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




More information about the lldb-commits mailing list