source: tools/gdb/utils-gdb.py @ 9987d79

arm-ehenumforall-pointer-decayjacob/cs343-translationnew-astnew-ast-unique-exprpthread-emulationqualifiedEnum
Last change on this file since 9987d79 was 9987d79, checked in by Thierry Delisle <tdelisle@…>, 3 years ago

Several fixes in the gdb support.
It is starting to be useful *and* convenient.

  • Property mode set to 100644
File size: 15.6 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 all_clusters():
112        if not is_cforall():
113                return None
114
115        cluster_root = get_cluster_root()
116        if cluster_root.address == 0x0:
117                return
118
119        curr = cluster_root
120        ret = [curr]
121
122        while True:
123                curr = curr['_X4nodeS26__cluster____dbg_node_cltr_1']['_X4nextPS7cluster_1']
124                if curr == cluster_root:
125                        break
126
127                ret.append(curr)
128
129        return ret
130
131
132def lookup_cluster(name = None):
133        """
134        Look up a cluster given its ID
135        @name: str
136        Return: gdb.Value
137        """
138        if not is_cforall():
139                return None
140
141        root = get_cluster_root()
142        if root.address == 0x0:
143                return None
144
145        if not name:
146                return root
147
148        # lookup for the task associated with the id
149        cluster = None
150        curr = root
151        while True:
152                if curr['_X4namePKc_1'].string() == name:
153                        cluster = curr.address
154                        break
155                curr = curr['_X4nodeS26__cluster____dbg_node_cltr_1']['_X4nextPS7cluster_1']
156                if curr == root or curr == 0x0:
157                        break
158
159        if not cluster:
160                print("Cannot find a cluster with the name: {}.".format(name))
161                return None
162
163        return cluster
164
165def lookup_threads_by_cluster(cluster):
166                # Iterate through a circular linked list of threads and accumulate them in an array
167                threads = []
168
169                cfa_t = get_cfa_types()
170                root = cluster['_X7threadsS8__dllist_S7$thread__1']['_X4headPY15__TYPE_generic__1'].cast(cfa_t.thread_ptr)
171
172                if root == 0x0 or root.address == 0x0:
173                        print('There are no tasks for cluster: {}'.format(cluster))
174                        return threads
175
176                curr = root
177                tid = 0
178                sid = -1
179
180                while True:
181                        t = ThreadInfo(cluster, curr)
182                        if t.is_system():
183                                t.tid = sid
184                                sid -= 1
185                        else:
186                                t.tid = tid
187                                tid += 1
188
189                        threads.append(t)
190
191                        curr = curr['node']['next']
192                        if curr == root or curr == 0x0:
193                                break
194
195                return threads
196
197def system_thread(thread):
198        return False
199
200def adjust_stack(pc, fp, sp):
201        # pop sp, fp, pc from global stack
202        gdb.execute('set $pc = {}'.format(pc))
203        gdb.execute('set $rbp = {}'.format(fp))
204        gdb.execute('set $sp = {}'.format(sp))
205
206############################ COMMAND IMPLEMENTATION #########################
207
208class Clusters(gdb.Command):
209        """Cforall: Display currently known clusters
210Usage:
211        info clusters                 : print out all the clusters
212"""
213
214        def __init__(self):
215                super(Clusters, self).__init__('info clusters', gdb.COMMAND_USER)
216
217        def print_cluster(self, cluster_name, cluster_address):
218                print('{:>20}  {:>20}'.format(cluster_name, cluster_address))
219
220        #entry point from gdb
221        def invoke(self, arg, from_tty):
222                if not is_cforall():
223                        return
224
225                if arg:
226                        print("info clusters does not take arguments")
227                        print_usage(self)
228                        return
229
230                self.print_cluster('Name', 'Address')
231
232                for c in all_clusters():
233                        self.print_cluster(c['_X4namePKc_1'].string(), str(c))
234
235                print("")
236
237############
238class Processors(gdb.Command):
239        """Cforall: Display currently known processors
240Usage:
241        info processors                 : print out all the processors in the Main Cluster
242        info processors all             : print out all processors in all clusters
243        info processors <cluster_name>  : print out all processors in a given cluster
244"""
245
246        def __init__(self):
247                super(Processors, self).__init__('info processors', gdb.COMMAND_USER)
248
249        def print_processor(self, name, status, pending, address):
250                print('{:>20}  {:>11}  {:>13}  {:>20}'.format(name, status, pending, address))
251
252        def iterate_procs(self, root, active):
253                if root == 0x0:
254                        return
255
256                cfa_t = get_cfa_types()
257                curr = root
258
259                while True:
260                        processor = curr
261                        should_stop = processor['_X12do_terminateVb_1']
262                        stop_count  = processor['_X10terminatedS9semaphore_1']['_X5counti_1']
263                        if not should_stop:
264                                status = 'Active' if active else 'Idle'
265                        else:
266                                status_str  = 'Last Thread' if stop_count >= 0 else 'Terminating'
267                                status      = '{}({},{})'.format(status_str, should_stop, stop_count)
268
269                        self.print_processor(processor['_X4namePKc_1'].string(),
270                                        status, str(processor['_X18pending_preemptionb_1']), str(processor)
271                                )
272
273                        curr = curr['_X4nodeS28__processor____dbg_node_proc_1']['_X4nextPS9processor_1']
274
275                        if curr == root or curr == 0x0:
276                                break
277
278        #entry point from gdb
279        def invoke(self, arg, from_tty):
280                if not is_cforall():
281                        return
282
283                if not arg:
284                        clusters = [lookup_cluster(None)]
285                elif arg == "all":
286                        clusters = all_clusters()
287                else:
288                        clusters = [lookup_cluster(arg)]
289
290                if not clusters:
291                        print("No Cluster matching arguments found")
292                        return
293
294                cfa_t = get_cfa_types()
295                for cluster in clusters:
296                        print('Cluster: "{}"({})'.format(cluster['_X4namePKc_1'].string(), cluster.cast(cfa_t.cluster_ptr)))
297
298                        active_root = cluster.cast(cfa_t.cluster_ptr) \
299                                        ['_X5procsS8__dllist_S9processor__1'] \
300                                        ['_X4headPY15__TYPE_generic__1'] \
301                                        .cast(cfa_t.processor_ptr)
302
303                        idle_root = cluster.cast(cfa_t.cluster_ptr) \
304                                        ['_X5idlesS8__dllist_S9processor__1'] \
305                                        ['_X4headPY15__TYPE_generic__1'] \
306                                        .cast(cfa_t.processor_ptr)
307
308                        if idle_root != 0x0 or active_root != 0x0:
309                                self.print_processor('Name', 'Status', 'Pending Yield', 'Address')
310                                self.iterate_procs(active_root, True)
311                                self.iterate_procs(idle_root, False)
312                        else:
313                                print("No processors on cluster")
314
315                print()
316
317############
318class Threads(gdb.Command):
319        """Cforall: Display currently known threads
320Usage:
321        cfathreads                           : print Main Cluster threads, application threads only
322        cfathreads all                       : print all clusters, all threads
323        cfathreads <clusterName>             : print cluster threads, application threads only
324        """
325        def __init__(self):
326                # The first parameter of the line below is the name of the command. You
327                # can call it 'uc++ task'
328                super(Threads, self).__init__('info cfathreads', gdb.COMMAND_USER)
329
330        def print_formatted(self, marked, tid, name, state, address):
331                print('{:>1}  {:>4}  {:>20}  {:>10}  {:>20}'.format('*' if marked else ' ', tid, name, state, address))
332
333        def print_thread(self, thread, tid, marked):
334                cfa_t = get_cfa_types()
335                self.print_formatted(marked, tid, thread['self_cor']['name'].string(), str(thread['state'].cast(cfa_t.thread_state)), str(thread))
336
337        def print_threads_by_cluster(self, cluster, print_system = False):
338                # Iterate through a circular linked list of tasks and print out its
339                # name along with address associated to each cluster
340                threads = lookup_threads_by_cluster(cluster)
341                if not threads:
342                        return
343
344                running_thread = find_curr_thread()
345                if running_thread is None:
346                        print('Could not identify current thread')
347
348                self.print_formatted(False, '', 'Name', 'State', 'Address')
349
350                for t in threads:
351                        if not t.is_system() or print_system:
352                                self.print_thread(t.value, t.tid, t.value == running_thread if running_thread else False)
353
354                print()
355
356        def print_all_threads(self):
357                for c in all_clusters():
358                        self.print_threads_by_cluster(c, False)
359
360        def invoke(self, arg, from_tty):
361                """
362                @arg: str
363                @from_tty: bool
364                """
365                if not is_cforall():
366                        return
367
368                if not arg:
369                        cluster = lookup_cluster()
370                        if not cluster:
371                                print("Could not find Main Cluster")
372                                return
373
374                        # only tasks and main
375                        self.print_threads_by_cluster(cluster, False)
376
377                elif arg == 'all':
378                        # all threads, all clusters
379                        self.print_all_threads()
380
381                else:
382                        cluster = lookup_cluster(arg)
383                        if not cluster:
384                                print("Could not find cluster '{}'".format(arg))
385                                return
386
387                        # all tasks, specified cluster
388                        self.print_threads_by_cluster(cluster, True)
389
390
391############
392class Thread(gdb.Command):
393        """Cforall: Switch to specified user threads
394Usage:
395        cfathread <id>                       : switch stack to thread id on main cluster
396        cfathread 0x<address>                : switch stack to thread on any cluster
397        cfathread <id> <clusterName>         : switch stack to thread on specified cluster
398        """
399        def __init__(self):
400                # The first parameter of the line below is the name of the command. You
401                # can call it 'uc++ task'
402                super(Thread, self).__init__('cfathread', gdb.COMMAND_USER)
403
404        ############################ AUXILIARY FUNCTIONS #########################
405
406        def switchto(self, thread):
407                """Change to a new task by switching to a different stack and manually
408                adjusting sp, fp and pc
409                @task_address: str
410                        2 supported format:
411                                in hex format
412                                        <hex_address>: literal hexadecimal address
413                                        Ex: 0xffffff
414                                in name of the pointer to the task
415                                        "task_name": pointer of the variable name of the cluster
416                                                Ex: T* s -> task_name = s
417                        Return: gdb.value of the cluster's address
418                """
419                try:
420                        if not gdb.lookup_symbol('__cfactx_switch'):
421                                print('__cfactx_switch symbol is unavailable')
422                                return
423                except:
424                        print('here 3')
425
426                cfa_t = get_cfa_types()
427
428                state = thread['state'].cast(cfa_t.thread_state)
429                try:
430                        if state == gdb.parse_and_eval('Halted'):
431                                print('Cannot switch to a terminated thread')
432                                return
433
434                        if state == gdb.parse_and_eval('Start'):
435                                print('Cannjot switch to a thread not yet run')
436                                return
437                except:
438                        print("here 2")
439                        return
440
441
442                context = thread['context']
443
444                # lookup for sp,fp and uSwitch
445                xsp = context['SP'] + 48
446                xfp = context['FP']
447
448                # convert string so we can strip out the address
449                try:
450                        xpc = get_addr(gdb.parse_and_eval('__cfactx_switch').address + 28)
451                except:
452                        print("here")
453                        return
454
455                # must be at frame 0 to set pc register
456                gdb.execute('select-frame 0')
457
458                # push sp, fp, pc into a global stack
459                global STACK
460                sp = gdb.parse_and_eval('$sp')
461                fp = gdb.parse_and_eval('$fp')
462                pc = gdb.parse_and_eval('$pc')
463                stack_info = StackInfo(sp = sp, fp = fp, pc = pc)
464                STACK.append(stack_info)
465
466                # update registers for new task
467                print('switching to ')
468                gdb.execute('set $rsp={}'.format(xsp))
469                gdb.execute('set $rbp={}'.format(xfp))
470                gdb.execute('set $pc={}'.format(xpc))
471
472        def find_matching_gdb_thread_id():
473                """
474                Parse the str from info thread to get the number
475                """
476                info_thread_str = gdb.execute('info thread', to_string=True).splitlines()
477                for thread_str in info_thread_str:
478                        if thread_str.find('this={}'.format(task)) != -1:
479                                thread_id_pattern = r'^\*?\s+(\d+)\s+Thread'
480                                # retrive gdb thread id
481                                return re.match(thread_id_pattern, thread_str).group(1)
482
483                        # check if the task is running or not
484                        if task_state == gdb.parse_and_eval('uBaseTask::Running'):
485                                # find the equivalent thread from info thread
486                                gdb_thread_id = find_matching_gdb_thread_id()
487                                if gdb_thread_id is None:
488                                        print('cannot find the thread id to switch to')
489                                        return
490                                # switch to that thread based using thread command
491                                gdb.execute('thread {}'.format(gdb_thread_id))
492
493        def switchto_id(self, tid, cluster):
494                """
495                @cluster: cluster object
496                @tid: int
497                """
498                threads = lookup_threads_by_cluster( cluster )
499
500                for t in threads:
501                        if t.tid == tid:
502                                self.switchto(t.value)
503                                return
504
505                print("Cound not find thread by id '{}'".format(tid))
506
507        def invoke(self, arg, from_tty):
508                """
509                @arg: str
510                @from_tty: bool
511                """
512                if not is_cforall():
513                        return
514
515                argv = parse(arg)
516                print(argv)
517                if argv[0].isdigit():
518                        cname = " ".join(argv[1:]) if len(argv) > 1 else None
519                        cluster = lookup_cluster(cname)
520                        if not cluster:
521                                print("Could not find cluster '{}'".format(cname if cname else "Main Cluster"))
522                                return
523
524                        try:
525                                tid = int(argv[0])
526                        except:
527                                print("'{}' not a valid thread id".format(argv[0]))
528                                print_usage(self)
529                                return
530
531                                # by id, userCluster
532                        self.switchto_id(tid, cluster)
533
534                elif argv[0].startswith('0x') or argv[0].startswith('0X'):
535                        self.switchto(argv[0]) # by address, any cluster
536
537############
538class PrevThread(gdb.Command):
539        """Switch back to previous task on the stack"""
540        usage_msg = 'prevtask'
541
542        def __init__(self):
543                super(PrevThread, self).__init__('prevtask', gdb.COMMAND_USER)
544
545        def invoke(self, arg, from_tty):
546                """
547                @arg: str
548                @from_tty: bool
549                """
550                global STACK
551                if len(STACK) != 0:
552                        # must be at frame 0 to set pc register
553                        gdb.execute('select-frame 0')
554
555                        # pop stack
556                        stack_info = STACK.pop()
557                        pc = get_addr(stack_info.pc)
558                        sp = stack_info.sp
559                        fp = stack_info.fp
560
561                        # pop sp, fp, pc from global stack
562                        adjust_stack(pc, fp, sp)
563
564                        # must be at C++ frame to access C++ vars
565                        gdb.execute('frame 1')
566                else:
567                        print('empty stack')
568
569class ResetOriginFrame(gdb.Command):
570        """Reset to the origin frame prior to continue execution again"""
571        usage_msg = 'resetOriginFrame'
572        def __init__(self):
573                super(ResetOriginFrame, self).__init__('reset', gdb.COMMAND_USER)
574
575        def invoke(self, arg, from_tty):
576                """
577                @arg: str
578                @from_tty: bool
579                """
580                global STACK
581                if len(STACK) != 0:
582                        stack_info = STACK.pop(0)
583                        STACK.clear()
584                        pc = get_addr(stack_info.pc)
585                        sp = stack_info.sp
586                        fp = stack_info.fp
587
588                        # pop sp, fp, pc from global stack
589                        adjust_stack(pc, fp, sp)
590
591                        # must be at C++ frame to access C++ vars
592                        gdb.execute('frame 1')
593                #else:
594                        #print('reset: empty stack') #probably does not have to print msg
595
596Clusters()
597Processors()
598ResetOriginFrame()
599PrevThread()
600Threads()
601Thread()
602
603# Local Variables: #
604# mode: Python #
605# End: #
Note: See TracBrowser for help on using the repository browser.