source: tools/gdb/utils-gdb.py @ bb75b4e

arm-ehjacob/cs343-translationnew-astnew-ast-unique-expr
Last change on this file since bb75b4e was bb75b4e, checked in by Thierry Delisle <tdelisle@…>, 19 months ago

Changed processors and clusters command to fit more closely info threads

  • Property mode set to 100644
File size: 24.0 KB
Line 
1#
2# Copyright (C) Lynn Tran, Jiachen Zhang 2018
3#
4# utils-gdb.py --
5#
6# Author           : Lynn Tran
7# Created On       : Mon Oct 1 22:06:09 2018
8# Last Modified By : Peter A. Buhr
9# Last Modified On : Sat Jan 19 14:16:10 2019
10# Update Count     : 11
11#
12
13"""
14To run this extension, the python name has to be as same as one of the loaded library
15Additionally, the file must exist in a folder which is in gdb's safe path
16"""
17import collections
18import gdb
19import re
20
21# set these signal handlers with some settings (nostop, noprint, pass)
22gdb.execute('handle SIGALRM nostop noprint pass')
23gdb.execute('handle SIGUSR1 nostop noprint pass')
24
25CfaTypes = collections.namedtuple('CfaTypes', 'cluster_ptr processor_ptr thread_ptr int_ptr thread_state')
26
27class Thread:
28    tid = 0
29    cluster = None
30    value = None
31
32    def __init__(self, cluster, value):
33        self.cluster = cluster
34        self.value = value
35
36    def is_system(self):
37        return False
38
39# A named tuple representing information about a stack
40StackInfo = collections.namedtuple('StackInfo', 'sp fp pc')
41
42# A global variable to keep track of stack information as one switches from one
43# task to another task
44STACK = []
45
46# A global variable to keep all system task name
47SysTask_Name = ["uLocalDebuggerReader", "uLocalDebugger", "uProcessorTask", "uBootTask", "uSystemTask",
48"uProcessorTask", "uPthread", "uProfiler"]
49
50not_supported_error_msg = "Not a supported command for this language"
51
52def is_cforall():
53    return True
54
55def get_cfa_types():
56    # GDB types for various structures/types in CFA
57    return CfaTypes(cluster_ptr = gdb.lookup_type('struct cluster').pointer(),
58                  processor_ptr = gdb.lookup_type('struct processor').pointer(),
59                     thread_ptr = gdb.lookup_type('struct $thread').pointer(),
60                        int_ptr = gdb.lookup_type('int').pointer(),
61                   thread_state = gdb.lookup_type('enum coroutine_state'))
62
63def get_addr(addr):
64    """
65    NOTE: sketchy solution to retrieve address. There is a better solution...
66    @addr: str of an address that can be in a format 0xfffff <type of the object
67    at this address>
68    Return: str of just the address
69    """
70    str_addr = str(addr)
71    ending_addr_index = str_addr.find('<')
72    if ending_addr_index == -1:
73        return str(addr)
74    return str_addr[:ending_addr_index].strip()
75
76def print_usage(obj):
77    print(obj.__doc__)
78
79def parse(args):
80    """
81    Split the argument list in string format, where each argument is separated
82    by whitespace delimiter, to a list of arguments like argv
83    @args: str of arguments
84    Return:
85        [] if args is an empty string
86        list if args is not empty
87    """
88    # parse the string format of arguments and return a list of arguments
89    argv = args.split(' ')
90    if len(argv) == 1 and argv[0] == '':
91        return []
92    return argv
93
94def get_cluster_root():
95    """
96    Return: gdb.Value of globalClusters.root (is an address)
97    """
98    cluster_root = gdb.parse_and_eval('_X11mainClusterPS7cluster_1')
99    if cluster_root.address == 0x0:
100        print('No clusters, program terminated')
101    return cluster_root
102
103def lookup_cluster(name = None):
104    """
105    Look up a cluster given its ID
106    @name: str
107    Return: gdb.Value
108    """
109    if not is_cforall():
110        return None
111
112    root = get_cluster_root()
113    if root.address == 0x0:
114        return None
115
116    if not name:
117        return root
118
119    # lookup for the task associated with the id
120    cluster = None
121    curr = root
122    while True:
123        if curr['_X4namePKc_1'].string() == name:
124            cluster = curr.address
125            break
126        curr = curr['_X4nodeS26__cluster____dbg_node_cltr_1']['_X4nextPS7cluster_1']
127        if curr == root or curr == 0x0:
128            break
129
130    if not cluster:
131        print("Cannot find a cluster with the name: {}.".format(name))
132        return None
133
134    return cluster
135
136def system_thread(thread):
137    return False
138
139def adjust_stack(pc, fp, sp):
140    # pop sp, fp, pc from global stack
141    gdb.execute('set $pc = {}'.format(pc))
142    gdb.execute('set $rbp = {}'.format(fp))
143    gdb.execute('set $sp = {}'.format(sp))
144
145############################ COMMAND IMPLEMENTATION #########################
146
147class Clusters(gdb.Command):
148    """Cforall: Display currently known clusters
149Usage:
150    info clusters                 : print out all the clusters
151"""
152
153    def __init__(self):
154        super(Clusters, self).__init__('info clusters', gdb.COMMAND_USER)
155
156    def print_cluster(self, cluster_name, cluster_address):
157        print('{:>20}  {:>20}'.format(cluster_name, cluster_address))
158
159    #entry point from gdb
160    def invoke(self, arg, from_tty):
161        if not is_cforall():
162            return
163
164        if arg:
165            print("info clusters does not take arguments")
166            print_usage(self)
167            return
168
169        cluster_root = get_cluster_root()
170        if cluster_root.address == 0x0:
171            return
172
173        curr = cluster_root
174        self.print_cluster('Name', 'Address')
175
176        while True:
177            self.print_cluster(curr['_X4namePKc_1'].string(), str(curr))
178            curr = curr['_X4nodeS26__cluster____dbg_node_cltr_1']['_X4nextPS7cluster_1']
179            if curr == cluster_root:
180                break
181
182        print("")
183
184############
185class Processors(gdb.Command):
186    """Cforall: Display currently known processors
187Usage:
188    info processors                 : print out all the processors in the Main Cluster
189    info processors <cluster_name>  : print out all processors in a given cluster
190"""
191
192    def __init__(self):
193        super(Processors, self).__init__('info processors', gdb.COMMAND_USER)
194
195    def print_processor(self, name, status, pending, address):
196        print('{:>20}  {:>11}  {:>13}  {:>20}'.format(name, status, pending, address))
197
198    def iterate_procs(self, root, active):
199        if root == 0x0:
200            return
201
202        cfa_t = get_cfa_types()
203        curr = root
204
205        while True:
206            processor = curr
207            should_stop = processor['_X12do_terminateVb_1']
208            stop_count  = processor['_X10terminatedS9semaphore_1']['_X5counti_1']
209            if not should_stop:
210                status = 'Active' if active else 'Idle'
211            else:
212                status_str  = 'Last Thread' if stop_count >= 0 else 'Terminating'
213                status      = '{}({},{})'.format(status_str, should_stop, stop_count)
214
215            self.print_processor(processor['_X4namePKc_1'].string(),
216                    status, str(processor['_X18pending_preemptionb_1']), str(processor)
217                )
218
219            curr = curr['_X4nodeS28__processor____dbg_node_proc_1']['_X4nextPS9processor_1']
220
221            if curr == root or curr == 0x0:
222                break
223
224    #entry point from gdb
225    def invoke(self, arg, from_tty):
226        if not is_cforall():
227            return
228
229        cluster = lookup_cluster(arg if arg else None)
230
231        if not cluster:
232            print("No Cluster matching arguments found")
233            return
234
235        cfa_t = get_cfa_types()
236        print('Cluster: "{}"({})'.format(cluster['_X4namePKc_1'].string(), cluster.cast(cfa_t.cluster_ptr)))
237
238        active_root = cluster.cast(cfa_t.cluster_ptr) \
239                ['_X5procsS8__dllist_S9processor__1'] \
240                ['_X4headPY15__TYPE_generic__1'] \
241                .cast(cfa_t.processor_ptr)
242
243        idle_root = cluster.cast(cfa_t.cluster_ptr) \
244                ['_X5idlesS8__dllist_S9processor__1'] \
245                ['_X4headPY15__TYPE_generic__1'] \
246                .cast(cfa_t.processor_ptr)
247
248        if idle_root != 0x0 or active_root != 0x0:
249            self.print_processor('Name', 'Status', 'Pending Yield', 'Address')
250            self.iterate_procs(active_root, True)
251            self.iterate_procs(idle_root, False)
252        else:
253            print("No processors on cluster")
254
255        print()
256
257############
258class Threads(gdb.Command):
259    def __init__(self):
260        # The first parameter of the line below is the name of the command. You
261        # can call it 'uc++ task'
262        super(Threads, self).__init__('info cfathreads', gdb.COMMAND_USER)
263
264    def print_usage(self):
265        print_usage("""
266    cfathread                            : print userCluster tasks, application tasks only
267    cfathread all                        : print all clusters, all tasks
268    cfathread <clusterName>              : print cluster tasks, application tasks only
269    """)
270
271    def invoke(self, arg, from_tty):
272        """
273        @arg: str
274        @from_tty: bool
275        """
276        if not is_cforall():
277            return
278
279        argv = parse(arg)
280        print(argv)
281        if len(argv) == 0:
282            cluster = lookup_cluster()
283            if not cluster:
284                print("Could not find Main Cluster")
285                return
286
287            # only tasks and main
288            self.print_threads_by_cluster(cluster, False)
289
290        elif len(argv) == 1:
291            if argv[0] == 'help':
292                self.print_usage()
293            # print tasks
294            elif argv[0] == 'all':
295                self.print_all_threads() # all tasks, all clusters
296            else:
297                """
298                Print out all the tasks available in the specified cluster
299                @cluster_name: str
300                """
301                print("cfathread by name")
302                cluster = lookup_cluster(argv[0])
303                if not cluster:
304                    return
305
306                # all tasks, specified cluster
307                self.print_threads_by_cluster(cluster, True)
308
309        elif len(argv) == 2:
310            # push task
311            self.pushtask_by_id(argv[0], argv[1]) # by id, specified cluster
312        else:
313            print('Invalid arguments')
314            self.print_usage()
315
316############
317class Thread(gdb.Command):
318    def __init__(self):
319        # The first parameter of the line below is the name of the command. You
320        # can call it 'uc++ task'
321        super(Threads, self).__init__('cfathread', gdb.COMMAND_USER)
322
323    def print_usage(self):
324        print_usage("""
325    cfathread                            : print userCluster tasks, application tasks only
326    cfathread <clusterName>              : print cluster tasks, application tasks only
327    cfathread all                        : print all clusters, all tasks
328    cfathread <id>                       : switch stack to thread id on userCluster
329    cfathread 0x<address>                    : switch stack to thread on any cluster
330    cfathread <id> <clusterName>         : switch stack to thread on specified cluster
331    """)
332
333    ############################ AUXILIARY FUNCTIONS #########################
334
335    def print_formatted(self, marked, tid, name, state, address):
336        print('{:>1}  {:>4}  {:>20}  {:>10}  {:>20}'.format('*' if marked else ' ', tid, name, state, address))
337
338    def print_thread(self, thread, tid, marked):
339        cfa_t = get_cfa_types()
340        self.print_formatted(marked, tid, thread['self_cor']['name'].string(), str(thread['state'].cast(cfa_t.thread_state)), str(thread))
341
342    def print_formatted_cluster(self, str_format, cluster_name, cluster_addr):
343        print(str_format.format(cluster_name, cluster_addr))
344
345    def find_curr_thread(self):
346        # btstr = gdb.execute('bt', to_string = True).splitlines()
347        # if len(btstr) == 0:
348        #     print('error')
349        #     return None
350        # return btstr[0].split('this=',1)[1].split(',')[0].split(')')[0]
351        return None
352
353    def print_tasks_by_cluster_all(self, cluster_address):
354        """
355        Display a list of all info about all available tasks on a particular cluster
356        @cluster_address: gdb.Value
357        """
358        cluster_address = cluster_address.cast(uCPPTypes.ucluster_ptr)
359        task_root = cluster_address['tasksOnCluster']['root']
360
361        if task_root == 0x0 or task_root.address == 0x0:
362            print('There are no tasks for cluster at address: {}'.format(cluster_address))
363            return
364
365        self.print_formatted_task('', 'Task Name', 'Address', 'State')
366        curr = task_root
367        task_id = 0
368        systask_id = -1
369
370        breakpoint_addr = self.find_curr_breakpoint_addr()
371        if breakpoint_addr is None:
372            return
373
374        while True:
375            global SysTask_Name
376            if (curr['task_']['name'].string() in SysTask_Name):
377                self.print_formatted_tasks(systask_id, breakpoint_addr, curr)
378                systask_id -= 1
379            else:
380                self.print_formatted_tasks(task_id, breakpoint_addr, curr)
381                task_id += 1
382
383            curr = curr['next'].cast(uCPPTypes.uBaseTaskDL_ptr_type)
384            if curr == task_root:
385                break
386
387    def print_tasks_by_cluster_address_all(self, cluster_address):
388        """
389        Display a list of all info about all available tasks on a particular cluster
390        @cluster_address: str
391        """
392        # Iterate through a circular linked list of tasks and print out its
393        # name along with address associated to each cluster
394
395        # convert hex string to hex number
396        try:
397            hex_addr = int(cluster_address, 16)
398        except:
399            self.print_usage()
400            return
401
402        cluster_address = gdb.Value(hex_addr)
403        if not self.print_tasks_by_cluster_all(cluster_address):
404            return
405
406    def threads_by_cluster(self, cluster):
407        # Iterate through a circular linked list of threads and accumulate them in an array
408        threads = []
409
410        cfa_t = get_cfa_types()
411        root = cluster['_X7threadsS8__dllist_S7$thread__1']['_X4headPY15__TYPE_generic__1'].cast(cfa_t.thread_ptr)
412
413        if root == 0x0 or root.address == 0x0:
414            print('There are no tasks for cluster: {}'.format(cluster))
415            return threads
416
417        curr = root
418        tid = 0
419        sid = -1
420
421        while True:
422            t = Thread(cluster, curr)
423            if t.is_system():
424                t.tid = sid
425                sid -= 1
426            else:
427                t.tid = tid
428                tid += 1
429
430            threads.append(t)
431
432            curr = curr['node']['next']
433            if curr == root or curr == 0x0:
434                break
435
436        return threads
437
438    def print_threads_by_cluster(self, cluster, print_system = False):
439        """
440        Display a list of limited info about all available threads on a particular cluster
441        @cluster: str
442        @print_system: bool
443        """
444        # Iterate through a circular linked list of tasks and print out its
445        # name along with address associated to each cluster
446
447        threads = self.threads_by_cluster(cluster)
448        if not threads:
449            return
450
451        running_thread = self.find_curr_thread()
452        if running_thread is None:
453            print('Could not identify current thread')
454
455        self.print_formatted(False, '', 'Name', 'State', 'Address')
456
457        for t in threads:
458            if not t.is_system() or print_system:
459                self.print_thread(t.value, t.tid, tivalue == running_thread if running_thread else False)
460
461        print()
462
463    ############################ COMMAND FUNCTIONS #########################
464
465    def print_all_threads(self):
466        """Iterate through each cluster, iterate through all tasks and  print out info about all the tasks
467        in those clusters"""
468        uCPPTypes = None
469        try:
470            uCPPTypes = get_uCPP_types()
471        except gdb.error:
472            print(not_supported_error_msg)
473            print(gdb.error)
474            return
475
476        cluster_root = get_cluster_root()
477        if cluster_root.address == 0x0:
478            return
479
480        curr = cluster_root
481        self.print_formatted_cluster(self.cluster_str_format, 'Cluster Name', 'Address')
482
483        while True:
484            addr = str(curr['cluster_'].reference_value())[1:]
485            self.print_formatted_cluster(self.cluster_str_format, curr['cluster_']['name'].string(), addr)
486
487            self.print_tasks_by_cluster_address_all(addr)
488            curr = curr['next'].cast(uCPPTypes.uClusterDL_ptr_type)
489            if curr == cluster_root:
490                break
491
492    def switchto(self, thread):
493        """Change to a new task by switching to a different stack and manually
494        adjusting sp, fp and pc
495        @task_address: str
496            2 supported format:
497                in hex format
498                    <hex_address>: literal hexadecimal address
499                    Ex: 0xffffff
500                in name of the pointer to the task
501                    "task_name": pointer of the variable name of the cluster
502                        Ex: T* s -> task_name = s
503            Return: gdb.value of the cluster's address
504        """
505        # uCPPTypes = None
506        # try:
507        #     uCPPTypes = get_uCPP_types()
508        # except gdb.error:
509        #     print(not_supported_error_msg)
510        #     print(gdb.error)
511        #     return
512
513        # # Task address has a format "task_address", which implies that it is the
514        # # name of the variable, and it needs to be evaluated
515        # if task_address.startswith('"') and task_address.endswith('"'):
516        #     task = gdb.parse_and_eval(task_address.replace('"', ''))
517        # else:
518        # # Task address format does not include the quotation marks, which implies
519        # # that it is a hex address
520        #     # convert hex string to hex number
521        #     try:
522        #         hex_addr = int(task_address, 16)
523        #     except:
524        #         self.print_usage()
525        #         return
526        #     task_address = gdb.Value(hex_addr)
527        #     task = task_address.cast(uCPPTypes.uBaseTask_ptr_type)
528        try:
529            if not gdb.lookup_symbol('__cfactx_switch'):
530                print('__cfactx_switch symbol is unavailable')
531                return
532        except:
533            print('here 3')
534
535        cfa_t = get_cfa_types()
536
537        state = thread['state'].cast(cfa_t.thread_state)
538        try:
539            if state == gdb.parse_and_eval('Halted'):
540                print('Cannot switch to a terminated thread')
541                return
542
543            if state == gdb.parse_and_eval('Start'):
544                print('Cannjot switch to a thread not yet run')
545                return
546        except:
547            print("here 2")
548            return
549
550
551        context = thread['context']
552
553        # lookup for sp,fp and uSwitch
554        xsp = context['SP'] + 48
555        xfp = context['FP']
556
557        # convert string so we can strip out the address
558        try:
559            xpc = get_addr(gdb.parse_and_eval('__cfactx_switch').address + 28)
560        except:
561            print("here")
562            return
563
564        # must be at frame 0 to set pc register
565        gdb.execute('select-frame 0')
566
567        # push sp, fp, pc into a global stack
568        global STACK
569        sp = gdb.parse_and_eval('$sp')
570        fp = gdb.parse_and_eval('$fp')
571        pc = gdb.parse_and_eval('$pc')
572        stack_info = StackInfo(sp = sp, fp = fp, pc = pc)
573        STACK.append(stack_info)
574
575        # update registers for new task
576        print('switching to ')
577        gdb.execute('set $rsp={}'.format(xsp))
578        gdb.execute('set $rbp={}'.format(xfp))
579        gdb.execute('set $pc={}'.format(xpc))
580
581    def find_matching_gdb_thread_id():
582        """
583        Parse the str from info thread to get the number
584        """
585        info_thread_str = gdb.execute('info thread', to_string=True).splitlines()
586        for thread_str in info_thread_str:
587            if thread_str.find('this={}'.format(task)) != -1:
588                thread_id_pattern = r'^\*?\s+(\d+)\s+Thread'
589                # retrive gdb thread id
590                return re.match(thread_id_pattern, thread_str).group(1)
591
592            # check if the task is running or not
593            if task_state == gdb.parse_and_eval('uBaseTask::Running'):
594                # find the equivalent thread from info thread
595                gdb_thread_id = find_matching_gdb_thread_id()
596                if gdb_thread_id is None:
597                    print('cannot find the thread id to switch to')
598                    return
599                # switch to that thread based using thread command
600                gdb.execute('thread {}'.format(gdb_thread_id))
601
602    def switchto_id(self, tid, cluster):
603        """
604        @cluster: cluster object
605        @tid: int
606        """
607        threads = self.threads_by_cluster( cluster )
608
609        for t in threads:
610            if t.tid == tid:
611                self.switchto(t.value)
612                return
613
614        print("Cound not find thread by id '{}'".format(tid))
615
616    def invoke(self, arg, from_tty):
617        """
618        @arg: str
619        @from_tty: bool
620        """
621        if not is_cforall():
622            return
623
624        argv = parse(arg)
625        print(argv)
626        if len(argv) == 0:
627            """
628            Iterate only Main Thread, print only tasks and main
629            """
630            cluster = lookup_cluster()
631            if not cluster:
632                print("Could not find Main Cluster")
633                return
634
635            # only tasks and main
636            self.print_threads_by_cluster(cluster, False)
637
638        elif len(argv) == 1:
639            if argv[0] == 'help':
640                self.print_usage()
641            # push task
642            elif argv[0].isdigit():
643                cluster = lookup_cluster()
644                if not cluster:
645                    print("Could not find Main Cluster")
646                    return
647
648                try:
649                    tid = int(argv[0])
650                except:
651                    print("'{}' not a valid thread id".format(argv[0]))
652                    self.print_usage()
653                    return
654
655                 # by id, userCluster
656                self.switchto_id(tid, cluster)
657
658            elif argv[0].startswith('0x') or argv[0].startswith('0X'):
659                self.switchto(argv[0]) # by address, any cluster
660            # print tasks
661            elif argv[0] == 'all':
662                self.print_all_threads() # all tasks, all clusters
663            else:
664                """
665                Print out all the tasks available in the specified cluster
666                @cluster_name: str
667                """
668                print("cfathread by name")
669                cluster = lookup_cluster(argv[0])
670                if not cluster:
671                    return
672
673                # all tasks, specified cluster
674                self.print_threads_by_cluster(cluster, True)
675
676        elif len(argv) == 2:
677            # push task
678            self.pushtask_by_id(argv[0], argv[1]) # by id, specified cluster
679        else:
680            print('Invalid arguments')
681            self.print_usage()
682
683############
684class PrevThread(gdb.Command):
685    """Switch back to previous task on the stack"""
686    usage_msg = 'prevtask'
687
688    def __init__(self):
689        super(PrevThread, self).__init__('prevtask', gdb.COMMAND_USER)
690
691    def invoke(self, arg, from_tty):
692        """
693        @arg: str
694        @from_tty: bool
695        """
696        global STACK
697        if len(STACK) != 0:
698            # must be at frame 0 to set pc register
699            gdb.execute('select-frame 0')
700
701            # pop stack
702            stack_info = STACK.pop()
703            pc = get_addr(stack_info.pc)
704            sp = stack_info.sp
705            fp = stack_info.fp
706
707            # pop sp, fp, pc from global stack
708            adjust_stack(pc, fp, sp)
709
710            # must be at C++ frame to access C++ vars
711            gdb.execute('frame 1')
712        else:
713            print('empty stack')
714
715class ResetOriginFrame(gdb.Command):
716    """Reset to the origin frame prior to continue execution again"""
717    usage_msg = 'resetOriginFrame'
718    def __init__(self):
719        super(ResetOriginFrame, self).__init__('reset', gdb.COMMAND_USER)
720
721    def invoke(self, arg, from_tty):
722        """
723        @arg: str
724        @from_tty: bool
725        """
726        global STACK
727        if len(STACK) != 0:
728            stack_info = STACK.pop(0)
729            STACK.clear()
730            pc = get_addr(stack_info.pc)
731            sp = stack_info.sp
732            fp = stack_info.fp
733
734            # pop sp, fp, pc from global stack
735            adjust_stack(pc, fp, sp)
736
737            # must be at C++ frame to access C++ vars
738            gdb.execute('frame 1')
739        #else:
740            #print('reset: empty stack') #probably does not have to print msg
741
742Clusters()
743Processors()
744ResetOriginFrame()
745PrevThread()
746Threads()
747
748# Local Variables: #
749# mode: Python #
750# End: #
Note: See TracBrowser for help on using the repository browser.