source: tools/gdb/utils-gdb.py @ 96df7c9c

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

Pushing intermediate to other machines

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