| [1cfdee9] | 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 | """
 | 
|---|
 | 14 | To run this extension, the python name has to be as same as one of the loaded library
 | 
|---|
 | 15 | Additionally, the file must exist in a folder which is in gdb's safe path
 | 
|---|
 | 16 | """
 | 
|---|
 | 17 | import collections
 | 
|---|
 | 18 | import gdb
 | 
|---|
 | 19 | import re
 | 
|---|
 | 20 | 
 | 
|---|
 | 21 | # set these signal handlers with some settings (nostop, noprint, pass)
 | 
|---|
 | 22 | gdb.execute('handle SIGALRM nostop noprint pass')
 | 
|---|
 | 23 | gdb.execute('handle SIGUSR1 nostop noprint pass')
 | 
|---|
 | 24 | 
 | 
|---|
| [6abcb4d] | 25 | CfaTypes = collections.namedtuple('CfaTypes', 'cluster_ptr processor_ptr thread_ptr int_ptr thread_state yield_state')
 | 
|---|
| [1cfdee9] | 26 | 
 | 
|---|
| [96df7c9c] | 27 | class ThreadInfo:
 | 
|---|
| [9987d79] | 28 |         tid = 0
 | 
|---|
 | 29 |         cluster = None
 | 
|---|
 | 30 |         value = None
 | 
|---|
| [1cfdee9] | 31 | 
 | 
|---|
| [9987d79] | 32 |         def __init__(self, cluster, value):
 | 
|---|
 | 33 |                 self.cluster = cluster
 | 
|---|
 | 34 |                 self.value = value
 | 
|---|
| [1cfdee9] | 35 | 
 | 
|---|
| [9987d79] | 36 |         def is_system(self):
 | 
|---|
 | 37 |                 return False
 | 
|---|
| [1cfdee9] | 38 | 
 | 
|---|
 | 39 | # A named tuple representing information about a stack
 | 
|---|
 | 40 | StackInfo = 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
 | 
|---|
 | 44 | STACK = []
 | 
|---|
 | 45 | 
 | 
|---|
 | 46 | not_supported_error_msg = "Not a supported command for this language"
 | 
|---|
 | 47 | 
 | 
|---|
 | 48 | def is_cforall():
 | 
|---|
| [9987d79] | 49 |         return True
 | 
|---|
| [1cfdee9] | 50 | 
 | 
|---|
 | 51 | def get_cfa_types():
 | 
|---|
| [9987d79] | 52 |         # GDB types for various structures/types in CFA
 | 
|---|
 | 53 |         return CfaTypes(cluster_ptr = gdb.lookup_type('struct cluster').pointer(),
 | 
|---|
| [6abcb4d] | 54 |                 processor_ptr = gdb.lookup_type('struct processor').pointer(),
 | 
|---|
| [b7d94ac5] | 55 |                 thread_ptr = gdb.lookup_type('struct thread$').pointer(),
 | 
|---|
| [6abcb4d] | 56 |                 int_ptr = gdb.lookup_type('int').pointer(),
 | 
|---|
 | 57 |                 thread_state = gdb.lookup_type('enum __Coroutine_State'),
 | 
|---|
 | 58 |                 yield_state = gdb.lookup_type('enum __Preemption_Reason'))
 | 
|---|
| [1cfdee9] | 59 | 
 | 
|---|
 | 60 | def get_addr(addr):
 | 
|---|
| [9987d79] | 61 |         """
 | 
|---|
 | 62 |         NOTE: sketchy solution to retrieve address. There is a better solution...
 | 
|---|
 | 63 |         @addr: str of an address that can be in a format 0xfffff <type of the object
 | 
|---|
 | 64 |         at this address>
 | 
|---|
 | 65 |         Return: str of just the address
 | 
|---|
 | 66 |         """
 | 
|---|
 | 67 |         str_addr = str(addr)
 | 
|---|
 | 68 |         ending_addr_index = str_addr.find('<')
 | 
|---|
 | 69 |         if ending_addr_index == -1:
 | 
|---|
 | 70 |                 return str(addr)
 | 
|---|
 | 71 |         return str_addr[:ending_addr_index].strip()
 | 
|---|
| [1cfdee9] | 72 | 
 | 
|---|
| [bb75b4e] | 73 | def print_usage(obj):
 | 
|---|
| [9987d79] | 74 |         print(obj.__doc__)
 | 
|---|
| [1cfdee9] | 75 | 
 | 
|---|
 | 76 | def parse(args):
 | 
|---|
| [9987d79] | 77 |         """
 | 
|---|
 | 78 |         Split the argument list in string format, where each argument is separated
 | 
|---|
 | 79 |         by whitespace delimiter, to a list of arguments like argv
 | 
|---|
 | 80 |         @args: str of arguments
 | 
|---|
 | 81 |         Return:
 | 
|---|
 | 82 |                 [] if args is an empty string
 | 
|---|
 | 83 |                 list if args is not empty
 | 
|---|
 | 84 |         """
 | 
|---|
 | 85 |         # parse the string format of arguments and return a list of arguments
 | 
|---|
 | 86 |         argv = args.split(' ')
 | 
|---|
 | 87 |         if len(argv) == 1 and argv[0] == '':
 | 
|---|
 | 88 |                 return []
 | 
|---|
 | 89 |         return argv
 | 
|---|
| [1cfdee9] | 90 | 
 | 
|---|
| [9dad5b3] | 91 | class ClusterIter:
 | 
|---|
 | 92 |         def __init__(self, root):
 | 
|---|
 | 93 |                 self.curr = None
 | 
|---|
 | 94 |                 self.root = root
 | 
|---|
 | 95 | 
 | 
|---|
 | 96 |         def __iter__(self):
 | 
|---|
 | 97 |                 return self
 | 
|---|
 | 98 | 
 | 
|---|
 | 99 |         def __next__(self):
 | 
|---|
 | 100 |                 # Clusters form a cycle
 | 
|---|
 | 101 |                 # If we haven't seen the root yet, then the root is the first
 | 
|---|
 | 102 |                 if not self.curr:
 | 
|---|
 | 103 |                         self.curr = self.root
 | 
|---|
 | 104 |                         return self.curr
 | 
|---|
 | 105 | 
 | 
|---|
 | 106 |                 # if we already saw the root, then go forward
 | 
|---|
 | 107 |                 self.curr = self.curr['_X4nodeS26__cluster____dbg_node_cltr_1']['_X4nextPS7cluster_1']
 | 
|---|
 | 108 | 
 | 
|---|
 | 109 |                 # if we reached the root again, then we are done
 | 
|---|
 | 110 |                 if self.curr == self.root:
 | 
|---|
 | 111 |                         raise StopIteration
 | 
|---|
 | 112 | 
 | 
|---|
 | 113 |                 # otherwise return the next
 | 
|---|
 | 114 |                 return self.curr
 | 
|---|
 | 115 | 
 | 
|---|
 | 116 | def all_clusters():
 | 
|---|
| [9987d79] | 117 |         """
 | 
|---|
| [9dad5b3] | 118 |         Return: a list of all the clusters as an iterator.
 | 
|---|
 | 119 |         obtained from gdb.Value of globalClusters.root (is an address)
 | 
|---|
| [9987d79] | 120 |         """
 | 
|---|
| [9dad5b3] | 121 |         if not is_cforall():
 | 
|---|
 | 122 |                 return []
 | 
|---|
 | 123 | 
 | 
|---|
| [9987d79] | 124 |         cluster_root = gdb.parse_and_eval('_X11mainClusterPS7cluster_1')
 | 
|---|
 | 125 |         if cluster_root.address == 0x0:
 | 
|---|
 | 126 |                 print('No clusters, program terminated')
 | 
|---|
| [9dad5b3] | 127 |                 return []
 | 
|---|
| [1cfdee9] | 128 | 
 | 
|---|
| [9dad5b3] | 129 |         return ClusterIter(cluster_root)
 | 
|---|
| [9987d79] | 130 | 
 | 
|---|
| [9dad5b3] | 131 | class ProcIter:
 | 
|---|
 | 132 |         def __init__(self, root):
 | 
|---|
 | 133 |                 self.curr = None
 | 
|---|
 | 134 |                 self.root = root
 | 
|---|
| [9987d79] | 135 | 
 | 
|---|
| [9dad5b3] | 136 |         def __iter__(self):
 | 
|---|
 | 137 |                 return self
 | 
|---|
| [9987d79] | 138 | 
 | 
|---|
| [9dad5b3] | 139 |         def check(self):
 | 
|---|
 | 140 |                 # check if this is the last value
 | 
|---|
 | 141 |                 addr = int(self.curr)
 | 
|---|
 | 142 |                 mask = 1 << ((8 * int(gdb.parse_and_eval('sizeof(void*)'))) - 1)
 | 
|---|
 | 143 |                 if 0 != (mask & addr):
 | 
|---|
 | 144 |                         raise StopIteration
 | 
|---|
| [9987d79] | 145 | 
 | 
|---|
| [9dad5b3] | 146 |         def __next__(self):
 | 
|---|
 | 147 |                 cfa_t = get_cfa_types()
 | 
|---|
| [9987d79] | 148 | 
 | 
|---|
| [9dad5b3] | 149 |                 # Processors form a cycle
 | 
|---|
 | 150 |                 # If we haven't seen the root yet, then the root is the first
 | 
|---|
 | 151 |                 if not self.curr:
 | 
|---|
 | 152 |                         my_next = self.root
 | 
|---|
 | 153 |                         self.curr = my_next.cast(cfa_t.processor_ptr)
 | 
|---|
| [9987d79] | 154 | 
 | 
|---|
| [9dad5b3] | 155 |                         #check if this is an empty list
 | 
|---|
 | 156 |                         self.check()
 | 
|---|
| [9987d79] | 157 | 
 | 
|---|
| [9dad5b3] | 158 |                         return self.curr
 | 
|---|
| [d8b17e2] | 159 | 
 | 
|---|
| [9dad5b3] | 160 |                 # if we already saw the root, then go forward
 | 
|---|
 | 161 |                 my_next = self.curr['__anonymous_object2225']['_X4nextPY13__tE_generic__1']
 | 
|---|
 | 162 |                 self.curr = my_next.cast(cfa_t.processor_ptr)
 | 
|---|
| [d8b17e2] | 163 | 
 | 
|---|
| [9dad5b3] | 164 |                 #check if we reached the end
 | 
|---|
 | 165 |                 self.check()
 | 
|---|
| [d8b17e2] | 166 | 
 | 
|---|
| [9dad5b3] | 167 |                 # otherwise return the next
 | 
|---|
 | 168 |                 return self.curr
 | 
|---|
| [d8b17e2] | 169 | 
 | 
|---|
| [9dad5b3] | 170 | def proc_list(cluster):
 | 
|---|
 | 171 |         """
 | 
|---|
 | 172 |         Return: for a given processor, return the active and idle processors, as 2 iterators
 | 
|---|
 | 173 |         """
 | 
|---|
 | 174 |         cfa_t = get_cfa_types()
 | 
|---|
 | 175 |         proclist = cluster['_X5procsS19__cluster_proc_list_1']
 | 
|---|
 | 176 |         idle = proclist['_X5idlesS5dlist_S9processorS5dlink_S9processor___1']['__anonymous_object2167']['_X4nextPY13__tE_generic__1']
 | 
|---|
 | 177 |         active = proclist['_X7activesS5dlist_S9processorS5dlink_S9processor___1']['__anonymous_object2167']['_X4nextPY13__tE_generic__1']
 | 
|---|
 | 178 |         return ProcIter(active.cast(cfa_t.processor_ptr)), ProcIter(idle.cast(cfa_t.processor_ptr))
 | 
|---|
| [d8b17e2] | 179 | 
 | 
|---|
| [9dad5b3] | 180 | def all_processors():
 | 
|---|
 | 181 |         procs = []
 | 
|---|
 | 182 |         for c in all_clusters():
 | 
|---|
 | 183 |                 active, idle = proc_list(c)
 | 
|---|
 | 184 |                 for p in active:
 | 
|---|
 | 185 |                         procs.append(p)
 | 
|---|
| [d8b17e2] | 186 | 
 | 
|---|
| [9dad5b3] | 187 |                 for p in idle:
 | 
|---|
 | 188 |                         procs.append(p)
 | 
|---|
 | 189 | 
 | 
|---|
 | 190 |         print(procs)
 | 
|---|
 | 191 |         return procs
 | 
|---|
| [d8b17e2] | 192 | 
 | 
|---|
 | 193 | def tls_for_pthread(pthrd):
 | 
|---|
 | 194 |         prev = gdb.selected_thread()
 | 
|---|
 | 195 |         inf = gdb.selected_inferior()
 | 
|---|
 | 196 | 
 | 
|---|
 | 197 |         thrd = inf.thread_from_thread_handle( pthrd )
 | 
|---|
 | 198 |         thrd.switch()
 | 
|---|
 | 199 |         tls = gdb.parse_and_eval('&_X9kernelTLSS16KernelThreadData_1')
 | 
|---|
 | 200 | 
 | 
|---|
 | 201 |         prev.switch()
 | 
|---|
 | 202 |         return tls
 | 
|---|
 | 203 | 
 | 
|---|
 | 204 | def tls_for_proc(proc):
 | 
|---|
| [9dad5b3] | 205 |         return proc['_X10local_dataPS16KernelThreadData_1']
 | 
|---|
| [d8b17e2] | 206 | 
 | 
|---|
 | 207 | def thread_for_pthread(pthrd):
 | 
|---|
| [b7d94ac5] | 208 |         return tls_for_pthread(pthrd)['_X11this_threadVPS7thread$_1']
 | 
|---|
| [d8b17e2] | 209 | 
 | 
|---|
 | 210 | def thread_for_proc(proc):
 | 
|---|
| [b7d94ac5] | 211 |         return tls_for_proc(proc)['_X11this_threadVPS7thread$_1']
 | 
|---|
| [d8b17e2] | 212 | 
 | 
|---|
 | 213 | 
 | 
|---|
 | 214 | 
 | 
|---|
 | 215 | def find_curr_thread():
 | 
|---|
 | 216 |         # btstr = gdb.execute('bt', to_string = True).splitlines()
 | 
|---|
 | 217 |         # if len(btstr) == 0:
 | 
|---|
 | 218 |         #     print('error')
 | 
|---|
 | 219 |         #     return None
 | 
|---|
 | 220 |         # return btstr[0].split('this=',1)[1].split(',')[0].split(')')[0]
 | 
|---|
 | 221 |         return None
 | 
|---|
| [96df7c9c] | 222 | 
 | 
|---|
| [1cfdee9] | 223 | def lookup_cluster(name = None):
 | 
|---|
| [9987d79] | 224 |         """
 | 
|---|
| [9dad5b3] | 225 |         Look up one or more cluster given a name
 | 
|---|
| [9987d79] | 226 |         @name: str
 | 
|---|
 | 227 |         Return: gdb.Value
 | 
|---|
 | 228 |         """
 | 
|---|
 | 229 |         if not is_cforall():
 | 
|---|
 | 230 |                 return None
 | 
|---|
 | 231 | 
 | 
|---|
| [9dad5b3] | 232 |         clusters = all_clusters()
 | 
|---|
 | 233 |         if not clusters:
 | 
|---|
| [9987d79] | 234 |                 return None
 | 
|---|
 | 235 | 
 | 
|---|
 | 236 |         if not name:
 | 
|---|
| [9dad5b3] | 237 |                 return clusters.root
 | 
|---|
| [9987d79] | 238 | 
 | 
|---|
 | 239 |         # lookup for the task associated with the id
 | 
|---|
| [9dad5b3] | 240 |         found = [c for c in clusters if c['_X4namePKc_1'].string() == name]
 | 
|---|
 | 241 | 
 | 
|---|
 | 242 |         if not found:
 | 
|---|
| [9987d79] | 243 |                 print("Cannot find a cluster with the name: {}.".format(name))
 | 
|---|
 | 244 |                 return None
 | 
|---|
 | 245 | 
 | 
|---|
| [9dad5b3] | 246 |         return found
 | 
|---|
 | 247 | 
 | 
|---|
| [1cfdee9] | 248 | 
 | 
|---|
| [96df7c9c] | 249 | def lookup_threads_by_cluster(cluster):
 | 
|---|
| [9987d79] | 250 |                 # Iterate through a circular linked list of threads and accumulate them in an array
 | 
|---|
 | 251 |                 threads = []
 | 
|---|
| [96df7c9c] | 252 | 
 | 
|---|
| [9987d79] | 253 |                 cfa_t = get_cfa_types()
 | 
|---|
| [b7d94ac5] | 254 |                 root = cluster['_X7threadsS8__dllist_S7thread$__1']['_X4headPY15__TYPE_generic__1'].cast(cfa_t.thread_ptr)
 | 
|---|
| [96df7c9c] | 255 | 
 | 
|---|
| [9987d79] | 256 |                 if root == 0x0 or root.address == 0x0:
 | 
|---|
 | 257 |                         print('There are no tasks for cluster: {}'.format(cluster))
 | 
|---|
 | 258 |                         return threads
 | 
|---|
| [96df7c9c] | 259 | 
 | 
|---|
| [9987d79] | 260 |                 curr = root
 | 
|---|
 | 261 |                 tid = 0
 | 
|---|
 | 262 |                 sid = -1
 | 
|---|
| [96df7c9c] | 263 | 
 | 
|---|
| [9987d79] | 264 |                 while True:
 | 
|---|
 | 265 |                         t = ThreadInfo(cluster, curr)
 | 
|---|
 | 266 |                         if t.is_system():
 | 
|---|
 | 267 |                                 t.tid = sid
 | 
|---|
 | 268 |                                 sid -= 1
 | 
|---|
 | 269 |                         else:
 | 
|---|
 | 270 |                                 t.tid = tid
 | 
|---|
 | 271 |                                 tid += 1
 | 
|---|
| [96df7c9c] | 272 | 
 | 
|---|
| [9987d79] | 273 |                         threads.append(t)
 | 
|---|
| [96df7c9c] | 274 | 
 | 
|---|
| [9987d79] | 275 |                         curr = curr['node']['next']
 | 
|---|
 | 276 |                         if curr == root or curr == 0x0:
 | 
|---|
 | 277 |                                 break
 | 
|---|
| [96df7c9c] | 278 | 
 | 
|---|
| [9987d79] | 279 |                 return threads
 | 
|---|
| [96df7c9c] | 280 | 
 | 
|---|
| [1cfdee9] | 281 | def system_thread(thread):
 | 
|---|
| [9987d79] | 282 |         return False
 | 
|---|
| [1cfdee9] | 283 | 
 | 
|---|
 | 284 | def adjust_stack(pc, fp, sp):
 | 
|---|
| [9987d79] | 285 |         # pop sp, fp, pc from global stack
 | 
|---|
 | 286 |         gdb.execute('set $pc = {}'.format(pc))
 | 
|---|
 | 287 |         gdb.execute('set $rbp = {}'.format(fp))
 | 
|---|
 | 288 |         gdb.execute('set $sp = {}'.format(sp))
 | 
|---|
| [1cfdee9] | 289 | 
 | 
|---|
 | 290 | ############################ COMMAND IMPLEMENTATION #########################
 | 
|---|
 | 291 | 
 | 
|---|
 | 292 | class Clusters(gdb.Command):
 | 
|---|
| [9987d79] | 293 |         """Cforall: Display currently known clusters
 | 
|---|
| [bb75b4e] | 294 | Usage:
 | 
|---|
| [9987d79] | 295 |         info clusters                 : print out all the clusters
 | 
|---|
| [bb75b4e] | 296 | """
 | 
|---|
| [1cfdee9] | 297 | 
 | 
|---|
| [9987d79] | 298 |         def __init__(self):
 | 
|---|
 | 299 |                 super(Clusters, self).__init__('info clusters', gdb.COMMAND_USER)
 | 
|---|
| [1cfdee9] | 300 | 
 | 
|---|
| [9987d79] | 301 |         def print_cluster(self, cluster_name, cluster_address):
 | 
|---|
 | 302 |                 print('{:>20}  {:>20}'.format(cluster_name, cluster_address))
 | 
|---|
| [1cfdee9] | 303 | 
 | 
|---|
| [9987d79] | 304 |         #entry point from gdb
 | 
|---|
 | 305 |         def invoke(self, arg, from_tty):
 | 
|---|
 | 306 |                 if not is_cforall():
 | 
|---|
 | 307 |                         return
 | 
|---|
| [1cfdee9] | 308 | 
 | 
|---|
| [9987d79] | 309 |                 if arg:
 | 
|---|
 | 310 |                         print("info clusters does not take arguments")
 | 
|---|
 | 311 |                         print_usage(self)
 | 
|---|
 | 312 |                         return
 | 
|---|
| [1cfdee9] | 313 | 
 | 
|---|
| [9987d79] | 314 |                 self.print_cluster('Name', 'Address')
 | 
|---|
| [1cfdee9] | 315 | 
 | 
|---|
| [9987d79] | 316 |                 for c in all_clusters():
 | 
|---|
 | 317 |                         self.print_cluster(c['_X4namePKc_1'].string(), str(c))
 | 
|---|
| [1cfdee9] | 318 | 
 | 
|---|
| [9987d79] | 319 |                 print("")
 | 
|---|
| [1cfdee9] | 320 | 
 | 
|---|
 | 321 | ############
 | 
|---|
 | 322 | class Processors(gdb.Command):
 | 
|---|
| [9987d79] | 323 |         """Cforall: Display currently known processors
 | 
|---|
| [bb75b4e] | 324 | Usage:
 | 
|---|
| [d8b17e2] | 325 |         info processors                 : print out all the processors
 | 
|---|
| [9987d79] | 326 |         info processors <cluster_name>  : print out all processors in a given cluster
 | 
|---|
| [bb75b4e] | 327 | """
 | 
|---|
| [1cfdee9] | 328 | 
 | 
|---|
| [9987d79] | 329 |         def __init__(self):
 | 
|---|
 | 330 |                 super(Processors, self).__init__('info processors', gdb.COMMAND_USER)
 | 
|---|
 | 331 | 
 | 
|---|
| [9dad5b3] | 332 |         def print_processor(self, processor, in_stats):
 | 
|---|
| [d8b17e2] | 333 |                 should_stop = processor['_X12do_terminateVb_1']
 | 
|---|
 | 334 |                 if not should_stop:
 | 
|---|
| [9dad5b3] | 335 |                         status = in_stats
 | 
|---|
| [d8b17e2] | 336 |                 else:
 | 
|---|
| [9987d79] | 337 |                         stop_count  = processor['_X10terminatedS9semaphore_1']['_X5counti_1']
 | 
|---|
| [d8b17e2] | 338 |                         status_str  = 'Last Thread' if stop_count >= 0 else 'Terminating'
 | 
|---|
 | 339 |                         status      = '{}({},{})'.format(status_str, should_stop, stop_count)
 | 
|---|
 | 340 | 
 | 
|---|
 | 341 |                 print('{:>20}  {:>11}  {:<7}  {:<}'.format(
 | 
|---|
 | 342 |                         processor['_X4namePKc_1'].string(),
 | 
|---|
 | 343 |                         status,
 | 
|---|
 | 344 |                         str(processor['_X18pending_preemptionb_1']),
 | 
|---|
 | 345 |                         str(processor)
 | 
|---|
 | 346 |                 ))
 | 
|---|
 | 347 |                 tls = tls_for_proc( processor )
 | 
|---|
| [b7d94ac5] | 348 |                 thrd = tls['_X11this_threadVPS7thread$_1']
 | 
|---|
| [d8b17e2] | 349 |                 if thrd != 0x0:
 | 
|---|
 | 350 |                         tname = '{} {}'.format(thrd['self_cor']['name'].string(), str(thrd))
 | 
|---|
 | 351 |                 else:
 | 
|---|
 | 352 |                         tname = None
 | 
|---|
| [9987d79] | 353 | 
 | 
|---|
| [d8b17e2] | 354 |                 print('{:>20}  {}'.format('Thread', tname))
 | 
|---|
 | 355 |                 print('{:>20}  {}'.format('TLS', tls))
 | 
|---|
| [9987d79] | 356 | 
 | 
|---|
 | 357 |         #entry point from gdb
 | 
|---|
 | 358 |         def invoke(self, arg, from_tty):
 | 
|---|
 | 359 |                 if not is_cforall():
 | 
|---|
 | 360 |                         return
 | 
|---|
 | 361 | 
 | 
|---|
 | 362 |                 if not arg:
 | 
|---|
 | 363 |                         clusters = all_clusters()
 | 
|---|
 | 364 |                 else:
 | 
|---|
 | 365 |                         clusters = [lookup_cluster(arg)]
 | 
|---|
 | 366 | 
 | 
|---|
 | 367 |                 if not clusters:
 | 
|---|
 | 368 |                         print("No Cluster matching arguments found")
 | 
|---|
 | 369 |                         return
 | 
|---|
 | 370 | 
 | 
|---|
| [d8b17e2] | 371 |                 print('{:>20}  {:>11}  {:<7}  {}'.format('Processor', '', 'Pending', 'Object'))
 | 
|---|
 | 372 |                 print('{:>20}  {:>11}  {:<7}  {}'.format('Name', 'Status', 'Yield', 'Address'))
 | 
|---|
| [9dad5b3] | 373 |                 for c in clusters:
 | 
|---|
 | 374 |                         print('Cluster {}'.format(c['_X4namePKc_1'].string()))
 | 
|---|
| [d8b17e2] | 375 | 
 | 
|---|
| [9dad5b3] | 376 |                         active, idle = proc_list(c)
 | 
|---|
| [d8b17e2] | 377 |                         # print the processor information
 | 
|---|
| [9dad5b3] | 378 |                         for p in active:
 | 
|---|
 | 379 |                                 self.print_processor(p, 'Active')
 | 
|---|
 | 380 | 
 | 
|---|
 | 381 |                         for p in idle:
 | 
|---|
 | 382 |                                 self.print_processor(p, 'Idle')
 | 
|---|
 | 383 | 
 | 
|---|
 | 384 |                         print()
 | 
|---|
| [9987d79] | 385 | 
 | 
|---|
 | 386 |                 print()
 | 
|---|
| [1cfdee9] | 387 | 
 | 
|---|
 | 388 | ############
 | 
|---|
 | 389 | class Threads(gdb.Command):
 | 
|---|
| [9987d79] | 390 |         """Cforall: Display currently known threads
 | 
|---|
| [96df7c9c] | 391 | Usage:
 | 
|---|
| [9987d79] | 392 |         cfathreads                           : print Main Cluster threads, application threads only
 | 
|---|
 | 393 |         cfathreads all                       : print all clusters, all threads
 | 
|---|
 | 394 |         cfathreads <clusterName>             : print cluster threads, application threads only
 | 
|---|
 | 395 |         """
 | 
|---|
 | 396 |         def __init__(self):
 | 
|---|
 | 397 |                 # The first parameter of the line below is the name of the command. You
 | 
|---|
 | 398 |                 # can call it 'uc++ task'
 | 
|---|
 | 399 |                 super(Threads, self).__init__('info cfathreads', gdb.COMMAND_USER)
 | 
|---|
 | 400 | 
 | 
|---|
 | 401 |         def print_formatted(self, marked, tid, name, state, address):
 | 
|---|
 | 402 |                 print('{:>1}  {:>4}  {:>20}  {:>10}  {:>20}'.format('*' if marked else ' ', tid, name, state, address))
 | 
|---|
 | 403 | 
 | 
|---|
 | 404 |         def print_thread(self, thread, tid, marked):
 | 
|---|
 | 405 |                 cfa_t = get_cfa_types()
 | 
|---|
| [6abcb4d] | 406 |                 ys = str(thread['preempted'].cast(cfa_t.yield_state))
 | 
|---|
 | 407 |                 if ys == '_X15__NO_PREEMPTIONKM19__Preemption_Reason_1':
 | 
|---|
 | 408 |                         state = str(thread['state'].cast(cfa_t.thread_state))
 | 
|---|
 | 409 |                 elif ys == '_X18__ALARM_PREEMPTIONKM19__Preemption_Reason_1':
 | 
|---|
 | 410 |                         state = 'preempted'
 | 
|---|
 | 411 |                 elif ys == '_X19__MANUAL_PREEMPTIONKM19__Preemption_Reason_1':
 | 
|---|
 | 412 |                         state = 'yield'
 | 
|---|
 | 413 |                 elif ys == '_X17__POLL_PREEMPTIONKM19__Preemption_Reason_1':
 | 
|---|
 | 414 |                         state = 'poll'
 | 
|---|
 | 415 |                 else:
 | 
|---|
 | 416 |                         print("error: thread {} in undefined preemption state {}".format(thread, ys))
 | 
|---|
 | 417 |                         state = 'error'
 | 
|---|
 | 418 |                 self.print_formatted(marked, tid, thread['self_cor']['name'].string(), state, str(thread))
 | 
|---|
| [9987d79] | 419 | 
 | 
|---|
 | 420 |         def print_threads_by_cluster(self, cluster, print_system = False):
 | 
|---|
 | 421 |                 # Iterate through a circular linked list of tasks and print out its
 | 
|---|
 | 422 |                 # name along with address associated to each cluster
 | 
|---|
 | 423 |                 threads = lookup_threads_by_cluster(cluster)
 | 
|---|
 | 424 |                 if not threads:
 | 
|---|
 | 425 |                         return
 | 
|---|
 | 426 | 
 | 
|---|
 | 427 |                 running_thread = find_curr_thread()
 | 
|---|
 | 428 |                 if running_thread is None:
 | 
|---|
 | 429 |                         print('Could not identify current thread')
 | 
|---|
 | 430 | 
 | 
|---|
 | 431 |                 self.print_formatted(False, '', 'Name', 'State', 'Address')
 | 
|---|
 | 432 | 
 | 
|---|
 | 433 |                 for t in threads:
 | 
|---|
 | 434 |                         if not t.is_system() or print_system:
 | 
|---|
 | 435 |                                 self.print_thread(t.value, t.tid, t.value == running_thread if running_thread else False)
 | 
|---|
 | 436 | 
 | 
|---|
 | 437 |                 print()
 | 
|---|
 | 438 | 
 | 
|---|
 | 439 |         def print_all_threads(self):
 | 
|---|
 | 440 |                 for c in all_clusters():
 | 
|---|
 | 441 |                         self.print_threads_by_cluster(c, False)
 | 
|---|
 | 442 | 
 | 
|---|
 | 443 |         def invoke(self, arg, from_tty):
 | 
|---|
 | 444 |                 """
 | 
|---|
 | 445 |                 @arg: str
 | 
|---|
 | 446 |                 @from_tty: bool
 | 
|---|
 | 447 |                 """
 | 
|---|
 | 448 |                 if not is_cforall():
 | 
|---|
 | 449 |                         return
 | 
|---|
 | 450 | 
 | 
|---|
 | 451 |                 if not arg:
 | 
|---|
 | 452 |                         cluster = lookup_cluster()
 | 
|---|
 | 453 |                         if not cluster:
 | 
|---|
 | 454 |                                 print("Could not find Main Cluster")
 | 
|---|
 | 455 |                                 return
 | 
|---|
 | 456 | 
 | 
|---|
 | 457 |                         # only tasks and main
 | 
|---|
 | 458 |                         self.print_threads_by_cluster(cluster, False)
 | 
|---|
 | 459 | 
 | 
|---|
 | 460 |                 elif arg == 'all':
 | 
|---|
 | 461 |                         # all threads, all clusters
 | 
|---|
 | 462 |                         self.print_all_threads()
 | 
|---|
 | 463 | 
 | 
|---|
 | 464 |                 else:
 | 
|---|
 | 465 |                         cluster = lookup_cluster(arg)
 | 
|---|
 | 466 |                         if not cluster:
 | 
|---|
| [9dad5b3] | 467 |                                 print("No matching cluster")
 | 
|---|
| [9987d79] | 468 |                                 return
 | 
|---|
 | 469 | 
 | 
|---|
 | 470 |                         # all tasks, specified cluster
 | 
|---|
 | 471 |                         self.print_threads_by_cluster(cluster, True)
 | 
|---|
| [96df7c9c] | 472 | 
 | 
|---|
| [bb75b4e] | 473 | 
 | 
|---|
 | 474 | ############
 | 
|---|
 | 475 | class Thread(gdb.Command):
 | 
|---|
| [9987d79] | 476 |         """Cforall: Switch to specified user threads
 | 
|---|
 | 477 | Usage:
 | 
|---|
 | 478 |         cfathread <id>                       : switch stack to thread id on main cluster
 | 
|---|
 | 479 |         cfathread 0x<address>                : switch stack to thread on any cluster
 | 
|---|
 | 480 |         cfathread <id> <clusterName>         : switch stack to thread on specified cluster
 | 
|---|
 | 481 |         """
 | 
|---|
 | 482 |         def __init__(self):
 | 
|---|
 | 483 |                 # The first parameter of the line below is the name of the command. You
 | 
|---|
 | 484 |                 # can call it 'uc++ task'
 | 
|---|
 | 485 |                 super(Thread, self).__init__('cfathread', gdb.COMMAND_USER)
 | 
|---|
 | 486 | 
 | 
|---|
 | 487 |         ############################ AUXILIARY FUNCTIONS #########################
 | 
|---|
 | 488 | 
 | 
|---|
 | 489 |         def switchto(self, thread):
 | 
|---|
 | 490 |                 """Change to a new task by switching to a different stack and manually
 | 
|---|
 | 491 |                 adjusting sp, fp and pc
 | 
|---|
 | 492 |                 @task_address: str
 | 
|---|
 | 493 |                         2 supported format:
 | 
|---|
 | 494 |                                 in hex format
 | 
|---|
 | 495 |                                         <hex_address>: literal hexadecimal address
 | 
|---|
 | 496 |                                         Ex: 0xffffff
 | 
|---|
 | 497 |                                 in name of the pointer to the task
 | 
|---|
 | 498 |                                         "task_name": pointer of the variable name of the cluster
 | 
|---|
 | 499 |                                                 Ex: T* s -> task_name = s
 | 
|---|
 | 500 |                         Return: gdb.value of the cluster's address
 | 
|---|
 | 501 |                 """
 | 
|---|
 | 502 |                 try:
 | 
|---|
 | 503 |                         if not gdb.lookup_symbol('__cfactx_switch'):
 | 
|---|
 | 504 |                                 print('__cfactx_switch symbol is unavailable')
 | 
|---|
 | 505 |                                 return
 | 
|---|
 | 506 |                 except:
 | 
|---|
 | 507 |                         print('here 3')
 | 
|---|
 | 508 | 
 | 
|---|
 | 509 |                 cfa_t = get_cfa_types()
 | 
|---|
 | 510 | 
 | 
|---|
 | 511 |                 state = thread['state'].cast(cfa_t.thread_state)
 | 
|---|
 | 512 |                 try:
 | 
|---|
 | 513 |                         if state == gdb.parse_and_eval('Halted'):
 | 
|---|
 | 514 |                                 print('Cannot switch to a terminated thread')
 | 
|---|
 | 515 |                                 return
 | 
|---|
 | 516 | 
 | 
|---|
 | 517 |                         if state == gdb.parse_and_eval('Start'):
 | 
|---|
 | 518 |                                 print('Cannjot switch to a thread not yet run')
 | 
|---|
 | 519 |                                 return
 | 
|---|
 | 520 |                 except:
 | 
|---|
 | 521 |                         print("here 2")
 | 
|---|
 | 522 |                         return
 | 
|---|
 | 523 | 
 | 
|---|
 | 524 | 
 | 
|---|
 | 525 |                 context = thread['context']
 | 
|---|
 | 526 | 
 | 
|---|
| [c0c0bd5] | 527 | 
 | 
|---|
 | 528 | 
 | 
|---|
 | 529 |                 # must be at frame 0 to set pc register
 | 
|---|
 | 530 |                 gdb.execute('select-frame 0')
 | 
|---|
 | 531 |                 if gdb.selected_frame().architecture().name() != 'i386:x86-64':
 | 
|---|
 | 532 |                         print('gdb debugging only supported for i386:x86-64 for now')
 | 
|---|
 | 533 |                         return
 | 
|---|
 | 534 | 
 | 
|---|
 | 535 |                 # gdb seems to handle things much better if we pretend we just entered the context switch
 | 
|---|
 | 536 |                 # pretend the pc is __cfactx_switch and adjust the sp, base pointer doesn't need to change
 | 
|---|
| [9987d79] | 537 |                 # lookup for sp,fp and uSwitch
 | 
|---|
| [c0c0bd5] | 538 |                 xsp = context['SP'] + 40 # 40 = 5 64bit registers : %r15, %r14, %r13, %r12, %rbx WARNING: x64 specific
 | 
|---|
| [9987d79] | 539 |                 xfp = context['FP']
 | 
|---|
 | 540 | 
 | 
|---|
 | 541 |                 # convert string so we can strip out the address
 | 
|---|
 | 542 |                 try:
 | 
|---|
| [c0c0bd5] | 543 |                         xpc = get_addr(gdb.parse_and_eval('__cfactx_switch').address)
 | 
|---|
| [9987d79] | 544 |                 except:
 | 
|---|
 | 545 |                         print("here")
 | 
|---|
 | 546 |                         return
 | 
|---|
 | 547 | 
 | 
|---|
 | 548 |                 # push sp, fp, pc into a global stack
 | 
|---|
 | 549 |                 global STACK
 | 
|---|
 | 550 |                 sp = gdb.parse_and_eval('$sp')
 | 
|---|
 | 551 |                 fp = gdb.parse_and_eval('$fp')
 | 
|---|
 | 552 |                 pc = gdb.parse_and_eval('$pc')
 | 
|---|
 | 553 |                 stack_info = StackInfo(sp = sp, fp = fp, pc = pc)
 | 
|---|
 | 554 |                 STACK.append(stack_info)
 | 
|---|
 | 555 | 
 | 
|---|
 | 556 |                 # update registers for new task
 | 
|---|
| [c0c0bd5] | 557 |                 # print('switching to {} ({}) : [{}, {}, {}]'.format(thread['self_cor']['name'].string(), str(thread), str(xsp), str(xfp), str(xpc)))
 | 
|---|
 | 558 |                 print('switching to thread {} ({})'.format(str(thread), thread['self_cor']['name'].string()))
 | 
|---|
| [9987d79] | 559 |                 gdb.execute('set $rsp={}'.format(xsp))
 | 
|---|
 | 560 |                 gdb.execute('set $rbp={}'.format(xfp))
 | 
|---|
 | 561 |                 gdb.execute('set $pc={}'.format(xpc))
 | 
|---|
 | 562 | 
 | 
|---|
 | 563 |         def find_matching_gdb_thread_id():
 | 
|---|
 | 564 |                 """
 | 
|---|
 | 565 |                 Parse the str from info thread to get the number
 | 
|---|
 | 566 |                 """
 | 
|---|
 | 567 |                 info_thread_str = gdb.execute('info thread', to_string=True).splitlines()
 | 
|---|
 | 568 |                 for thread_str in info_thread_str:
 | 
|---|
 | 569 |                         if thread_str.find('this={}'.format(task)) != -1:
 | 
|---|
 | 570 |                                 thread_id_pattern = r'^\*?\s+(\d+)\s+Thread'
 | 
|---|
 | 571 |                                 # retrive gdb thread id
 | 
|---|
 | 572 |                                 return re.match(thread_id_pattern, thread_str).group(1)
 | 
|---|
 | 573 | 
 | 
|---|
 | 574 |                         # check if the task is running or not
 | 
|---|
 | 575 |                         if task_state == gdb.parse_and_eval('uBaseTask::Running'):
 | 
|---|
 | 576 |                                 # find the equivalent thread from info thread
 | 
|---|
 | 577 |                                 gdb_thread_id = find_matching_gdb_thread_id()
 | 
|---|
 | 578 |                                 if gdb_thread_id is None:
 | 
|---|
 | 579 |                                         print('cannot find the thread id to switch to')
 | 
|---|
 | 580 |                                         return
 | 
|---|
 | 581 |                                 # switch to that thread based using thread command
 | 
|---|
 | 582 |                                 gdb.execute('thread {}'.format(gdb_thread_id))
 | 
|---|
 | 583 | 
 | 
|---|
 | 584 |         def switchto_id(self, tid, cluster):
 | 
|---|
 | 585 |                 """
 | 
|---|
 | 586 |                 @cluster: cluster object
 | 
|---|
 | 587 |                 @tid: int
 | 
|---|
 | 588 |                 """
 | 
|---|
 | 589 |                 threads = lookup_threads_by_cluster( cluster )
 | 
|---|
 | 590 | 
 | 
|---|
 | 591 |                 for t in threads:
 | 
|---|
 | 592 |                         if t.tid == tid:
 | 
|---|
 | 593 |                                 self.switchto(t.value)
 | 
|---|
 | 594 |                                 return
 | 
|---|
 | 595 | 
 | 
|---|
 | 596 |                 print("Cound not find thread by id '{}'".format(tid))
 | 
|---|
 | 597 | 
 | 
|---|
 | 598 |         def invoke(self, arg, from_tty):
 | 
|---|
 | 599 |                 """
 | 
|---|
 | 600 |                 @arg: str
 | 
|---|
 | 601 |                 @from_tty: bool
 | 
|---|
 | 602 |                 """
 | 
|---|
 | 603 |                 if not is_cforall():
 | 
|---|
 | 604 |                         return
 | 
|---|
 | 605 | 
 | 
|---|
 | 606 |                 argv = parse(arg)
 | 
|---|
 | 607 |                 if argv[0].isdigit():
 | 
|---|
 | 608 |                         cname = " ".join(argv[1:]) if len(argv) > 1 else None
 | 
|---|
 | 609 |                         cluster = lookup_cluster(cname)
 | 
|---|
 | 610 |                         if not cluster:
 | 
|---|
 | 611 |                                 print("Could not find cluster '{}'".format(cname if cname else "Main Cluster"))
 | 
|---|
 | 612 |                                 return
 | 
|---|
 | 613 | 
 | 
|---|
 | 614 |                         try:
 | 
|---|
 | 615 |                                 tid = int(argv[0])
 | 
|---|
 | 616 |                         except:
 | 
|---|
 | 617 |                                 print("'{}' not a valid thread id".format(argv[0]))
 | 
|---|
 | 618 |                                 print_usage(self)
 | 
|---|
 | 619 |                                 return
 | 
|---|
 | 620 | 
 | 
|---|
 | 621 |                                 # by id, userCluster
 | 
|---|
 | 622 |                         self.switchto_id(tid, cluster)
 | 
|---|
 | 623 | 
 | 
|---|
 | 624 |                 elif argv[0].startswith('0x') or argv[0].startswith('0X'):
 | 
|---|
 | 625 |                         self.switchto(argv[0]) # by address, any cluster
 | 
|---|
| [1cfdee9] | 626 | 
 | 
|---|
 | 627 | ############
 | 
|---|
 | 628 | class PrevThread(gdb.Command):
 | 
|---|
| [9987d79] | 629 |         """Switch back to previous task on the stack"""
 | 
|---|
 | 630 |         usage_msg = 'prevtask'
 | 
|---|
 | 631 | 
 | 
|---|
 | 632 |         def __init__(self):
 | 
|---|
 | 633 |                 super(PrevThread, self).__init__('prevtask', gdb.COMMAND_USER)
 | 
|---|
 | 634 | 
 | 
|---|
 | 635 |         def invoke(self, arg, from_tty):
 | 
|---|
 | 636 |                 """
 | 
|---|
 | 637 |                 @arg: str
 | 
|---|
 | 638 |                 @from_tty: bool
 | 
|---|
 | 639 |                 """
 | 
|---|
 | 640 |                 global STACK
 | 
|---|
 | 641 |                 if len(STACK) != 0:
 | 
|---|
 | 642 |                         # must be at frame 0 to set pc register
 | 
|---|
 | 643 |                         gdb.execute('select-frame 0')
 | 
|---|
 | 644 | 
 | 
|---|
 | 645 |                         # pop stack
 | 
|---|
 | 646 |                         stack_info = STACK.pop()
 | 
|---|
 | 647 |                         pc = get_addr(stack_info.pc)
 | 
|---|
 | 648 |                         sp = stack_info.sp
 | 
|---|
 | 649 |                         fp = stack_info.fp
 | 
|---|
 | 650 | 
 | 
|---|
 | 651 |                         # pop sp, fp, pc from global stack
 | 
|---|
 | 652 |                         adjust_stack(pc, fp, sp)
 | 
|---|
 | 653 | 
 | 
|---|
 | 654 |                         # must be at C++ frame to access C++ vars
 | 
|---|
 | 655 |                         gdb.execute('frame 1')
 | 
|---|
 | 656 |                 else:
 | 
|---|
 | 657 |                         print('empty stack')
 | 
|---|
| [1cfdee9] | 658 | 
 | 
|---|
 | 659 | class ResetOriginFrame(gdb.Command):
 | 
|---|
| [9987d79] | 660 |         """Reset to the origin frame prior to continue execution again"""
 | 
|---|
 | 661 |         usage_msg = 'resetOriginFrame'
 | 
|---|
 | 662 |         def __init__(self):
 | 
|---|
 | 663 |                 super(ResetOriginFrame, self).__init__('reset', gdb.COMMAND_USER)
 | 
|---|
 | 664 | 
 | 
|---|
 | 665 |         def invoke(self, arg, from_tty):
 | 
|---|
 | 666 |                 """
 | 
|---|
 | 667 |                 @arg: str
 | 
|---|
 | 668 |                 @from_tty: bool
 | 
|---|
 | 669 |                 """
 | 
|---|
 | 670 |                 global STACK
 | 
|---|
 | 671 |                 if len(STACK) != 0:
 | 
|---|
 | 672 |                         stack_info = STACK.pop(0)
 | 
|---|
 | 673 |                         STACK.clear()
 | 
|---|
 | 674 |                         pc = get_addr(stack_info.pc)
 | 
|---|
 | 675 |                         sp = stack_info.sp
 | 
|---|
 | 676 |                         fp = stack_info.fp
 | 
|---|
 | 677 | 
 | 
|---|
 | 678 |                         # pop sp, fp, pc from global stack
 | 
|---|
 | 679 |                         adjust_stack(pc, fp, sp)
 | 
|---|
 | 680 | 
 | 
|---|
 | 681 |                         # must be at C++ frame to access C++ vars
 | 
|---|
 | 682 |                         gdb.execute('frame 1')
 | 
|---|
 | 683 |                 #else:
 | 
|---|
 | 684 |                         #print('reset: empty stack') #probably does not have to print msg
 | 
|---|
| [1cfdee9] | 685 | 
 | 
|---|
 | 686 | Clusters()
 | 
|---|
 | 687 | Processors()
 | 
|---|
 | 688 | ResetOriginFrame()
 | 
|---|
 | 689 | PrevThread()
 | 
|---|
 | 690 | Threads()
 | 
|---|
| [9987d79] | 691 | Thread()
 | 
|---|
| [1cfdee9] | 692 | 
 | 
|---|
 | 693 | # Local Variables: #
 | 
|---|
 | 694 | # mode: Python #
 | 
|---|
 | 695 | # End: #
 | 
|---|