reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
#############################################################################
# This script contains two trivial examples of simple "scripted step" classes.
# To fully understand how the lldb "Thread Plan" architecture works, read the
# comments at the beginning of ThreadPlan.h in the lldb sources.  The python
# interface is a reduced version of the full internal mechanism, but captures
# most of the power with a much simpler interface.
#
# But I'll attempt a brief summary here.
# Stepping in lldb is done independently for each thread.  Moreover, the stepping
# operations are stackable.  So for instance if you did a "step over", and in
# the course of stepping over you hit a breakpoint, stopped and stepped again,
# the first "step-over" would be suspended, and the new step operation would
# be enqueued.  Then if that step over caused the program to hit another breakpoint,
# lldb would again suspend the second step and return control to the user, so
# now there are two pending step overs.  Etc. with all the other stepping
# operations.  Then if you hit "continue" the bottom-most step-over would complete,
# and another continue would complete the first "step-over".
#
# lldb represents this system with a stack of "Thread Plans".  Each time a new
# stepping operation is requested, a new plan is pushed on the stack.  When the
# operation completes, it is pushed off the stack.
#
# The bottom-most plan in the stack is the immediate controller of stepping,
# most importantly, when the process resumes, the bottom most plan will get
# asked whether to set the program running freely, or to instruction-single-step
# the current thread.  In the scripted interface, you indicate this by returning
# False or True respectively from the should_step method.
#
# Each time the process stops the thread plan stack for each thread that stopped
# "for a reason", Ii.e. a single-step completed on that thread, or a breakpoint
# was hit), is queried to determine how to proceed, starting from the most
# recently pushed plan, in two stages:
#
# 1) Each plan is asked if it "explains" the stop.  The first plan to claim the
#    stop wins.  In scripted Thread Plans, this is done by returning True from
#    the "explains_stop method.  This is how, for instance, control is returned
#    to the User when the "step-over" plan hits a breakpoint.  The step-over
#    plan doesn't explain the breakpoint stop, so it returns false, and the
#    breakpoint hit is propagated up the stack to the "base" thread plan, which
#    is the one that handles random breakpoint hits.
#
# 2) Then the plan that won the first round is asked if the process should stop.
#    This is done in the "should_stop" method.  The scripted plans actually do
#    three jobs in should_stop:
#      a) They determine if they have completed their job or not.  If they have
#         they indicate that by calling SetPlanComplete on their thread plan.
#      b) They decide whether they want to return control to the user or not.
#         They do this by returning True or False respectively.
#      c) If they are not done, they set up whatever machinery they will use
#         the next time the thread continues.
#
#    Note that deciding to return control to the user, and deciding your plan
#    is done, are orthgonal operations.  You could set up the next phase of
#    stepping, and then return True from should_stop, and when the user next
#    "continued" the process your plan would resume control.  Of course, the
#    user might also "step-over" or some other operation that would push a
#    different plan, which would take control till it was done.
#
#    One other detail you should be aware of, if the plan below you on the
#    stack was done, then it will be popped and the next plan will take control
#    and its "should_stop" will be called.
#
#    Note also, there should be another method called when your plan is popped,
#    to allow you to do whatever cleanup is required.  I haven't gotten to that
#    yet.  For now you should do that at the same time you mark your plan complete.
#
# 3) After the round of negotiation over whether to stop or not is done, all the
#    plans get asked if they are "stale".  If they are say they are stale
#    then they will get popped.  This question is asked with the "is_stale" method.
#
#    This is useful, for instance, in the FinishPrintAndContinue plan.  What might
#    happen here is that after continuing but before the finish is done, the program
#    could hit another breakpoint and stop.  Then the user could use the step
#    command repeatedly until they leave the frame of interest by stepping.
#    In that case, the step plan is the one that will be responsible for stopping,
#    and the finish plan won't be asked should_stop, it will just be asked if it
#    is stale.  In this case, if the step_out plan that the FinishPrintAndContinue
#    plan is driving is stale, so is ours, and it is time to do our printing.
#
# Both examples show stepping through an address range for 20 bytes from the
# current PC.  The first one does it by single stepping and checking a condition.
# It doesn't, however handle the case where you step into another frame while
# still in the current range in the starting frame.
#
# That is better handled in the second example by using the built-in StepOverRange
# thread plan.
#
# To use these stepping modes, you would do:
#
#     (lldb) command script import scripted_step.py
#     (lldb) thread step-scripted -C scripted_step.SimpleStep
# or
#
#     (lldb) thread step-scripted -C scripted_step.StepWithPlan

from __future__ import print_function

import lldb


class SimpleStep:

    def __init__(self, thread_plan, dict):
        self.thread_plan = thread_plan
        self.start_address = thread_plan.GetThread().GetFrameAtIndex(0).GetPC()

    def explains_stop(self, event):
        # We are stepping, so if we stop for any other reason, it isn't
        # because of us.
        if self.thread_plan.GetThread().GetStopReason() == lldb.eStopReasonTrace:
            return True
        else:
            return False

    def should_stop(self, event):
        cur_pc = self.thread_plan.GetThread().GetFrameAtIndex(0).GetPC()

        if cur_pc < self.start_address or cur_pc >= self.start_address + 20:
            self.thread_plan.SetPlanComplete(True)
            return True
        else:
            return False

    def should_step(self):
        return True


class StepWithPlan:

    def __init__(self, thread_plan, dict):
        self.thread_plan = thread_plan
        self.start_address = thread_plan.GetThread().GetFrameAtIndex(0).GetPCAddress()
        self.step_thread_plan = thread_plan.QueueThreadPlanForStepOverRange(
            self.start_address, 20)

    def explains_stop(self, event):
        # Since all I'm doing is running a plan, I will only ever get askedthis
        # if myplan doesn't explain the stop, and in that caseI don'teither.
        return False

    def should_stop(self, event):
        if self.step_thread_plan.IsPlanComplete():
            self.thread_plan.SetPlanComplete(True)
            return True
        else:
            return False

    def should_step(self):
        return False

# Here's another example which does "step over" through the current function,
# and when it stops at each line, it checks some condition (in this example the
# value of a variable) and stops if that condition is true.


class StepCheckingCondition:

    def __init__(self, thread_plan, dict):
        self.thread_plan = thread_plan
        self.start_frame = thread_plan.GetThread().GetFrameAtIndex(0)
        self.queue_next_plan()

    def queue_next_plan(self):
        cur_frame = self.thread_plan.GetThread().GetFrameAtIndex(0)
        cur_line_entry = cur_frame.GetLineEntry()
        start_address = cur_line_entry.GetStartAddress()
        end_address = cur_line_entry.GetEndAddress()
        line_range = end_address.GetFileAddress() - start_address.GetFileAddress()
        self.step_thread_plan = self.thread_plan.QueueThreadPlanForStepOverRange(
            start_address, line_range)

    def explains_stop(self, event):
        # We are stepping, so if we stop for any other reason, it isn't
        # because of us.
        return False

    def should_stop(self, event):
        if not self.step_thread_plan.IsPlanComplete():
            return False

        frame = self.thread_plan.GetThread().GetFrameAtIndex(0)
        if not self.start_frame.IsEqual(frame):
            self.thread_plan.SetPlanComplete(True)
            return True

        # This part checks the condition.  In this case we are expecting
        # some integer variable called "a", and will stop when it is 20.
        a_var = frame.FindVariable("a")

        if not a_var.IsValid():
            print("A was not valid.")
            return True

        error = lldb.SBError()
        a_value = a_var.GetValueAsSigned(error)
        if not error.Success():
            print("A value was not good.")
            return True

        if a_value == 20:
            self.thread_plan.SetPlanComplete(True)
            return True
        else:
            self.queue_next_plan()
            return False

    def should_step(self):
        return True

# Here's an example that steps out of the current frame, gathers some information
# and then continues.  The information in this case is rax.  Currently the thread
# plans are not a safe place to call lldb command-line commands, so the information
# is gathered through SB API calls.


class FinishPrintAndContinue:

    def __init__(self, thread_plan, dict):
        self.thread_plan = thread_plan
        self.step_out_thread_plan = thread_plan.QueueThreadPlanForStepOut(
            0, True)
        self.thread = self.thread_plan.GetThread()

    def is_stale(self):
        if self.step_out_thread_plan.IsPlanStale():
            self.do_print()
            return True
        else:
            return False

    def explains_stop(self, event):
        return False

    def should_stop(self, event):
        if self.step_out_thread_plan.IsPlanComplete():
            self.do_print()
            self.thread_plan.SetPlanComplete(True)
        return False

    def do_print(self):
        frame_0 = self.thread.frames[0]
        rax_value = frame_0.FindRegister("rax")
        if rax_value.GetError().Success():
            print("RAX on exit: ", rax_value.GetValue())
        else:
            print("Couldn't get rax value:", rax_value.GetError().GetCString())