1 #!/usr/bin/python
2
3 # Copyright (C) 2013 Oracle.
4 #
5 # Licensed under the Open Software License version 1.1
6
7 import sqlite3
8 import sys
9 import re
10 import subprocess
11
12 try:
13 con = sqlite3.connect('smatch_db.sqlite')
14 except sqlite3.Error, e:
15 print "Error %s:" % e.args[0]
16 sys.exit(1)
17
18 def usage():
19 print "%s" %(sys.argv[0])
20 print "<function> - how a function is called"
21 print "info <type> - how a function is called, filtered by type"
22 print "return_states <function> - what a function returns"
23 print "call_tree <function> - show the call tree"
24 print "where <struct_type> <member> - where a struct member is set"
25 print "type_size <struct_type> <member> - how a struct member is allocated"
26 print "data_info <struct_type> <member> - information about a given data type"
27 print "function_ptr <function> - which function pointers point to this"
28 print "trace_param <function> <param> - trace where a parameter came from"
29 print "find_tagged <function> <param> - find the source of a tagged value (arm64)"
30 print "parse_warns_tagged <smatch_warns.txt> - parse warns file for summary of tagged issues (arm64)"
31 print "locals <file> - print the local values in a file."
32 sys.exit(1)
33
34 function_ptrs = []
35 searched_ptrs = []
36 def get_function_pointers_helper(func):
37 cur = con.cursor()
38 cur.execute("select distinct ptr from function_ptr where function = '%s';" %(func))
39 for row in cur:
40 ptr = row[0]
41 if ptr in function_ptrs:
42 continue
43 function_ptrs.append(ptr)
44 if not ptr in searched_ptrs:
45 searched_ptrs.append(ptr)
46 get_function_pointers_helper(ptr)
47
48 def get_function_pointers(func):
49 global function_ptrs
50 global searched_ptrs
51 function_ptrs = [func]
52 searched_ptrs = [func]
53 get_function_pointers_helper(func)
54 return function_ptrs
55
56 db_types = { 0: "INTERNAL",
57 101: "PARAM_CLEARED",
58 103: "PARAM_LIMIT",
59 104: "PARAM_FILTER",
60 1001: "PARAM_VALUE",
61 1002: "BUF_SIZE",
62 1004: "CAPPED_DATA",
63 1005: "RETURN_VALUE",
64 1006: "DEREFERENCE",
65 1007: "RANGE_CAP",
66 1008: "LOCK_HELD",
67 1009: "LOCK_RELEASED",
68 1010: "ABSOLUTE_LIMITS",
69 1012: "PARAM_ADD",
70 1013: "PARAM_FREED",
71 1014: "DATA_SOURCE",
72 1015: "FUZZY_MAX",
73 1016: "STR_LEN",
74 1017: "ARRAY_LEN",
75 1018: "CAPABLE",
76 1019: "NS_CAPABLE",
77 1022: "TYPE_LINK",
78 1023: "UNTRACKED_PARAM",
79 1024: "CULL_PATH",
80 1025: "PARAM_SET",
81 1026: "PARAM_USED",
82 1027: "BYTE_UNITS",
83 1028: "COMPARE_LIMIT",
84 1029: "PARAM_COMPARE",
85 1030: "EXPECTS_TYPE",
86 1031: "CONSTRAINT",
87 1032: "PASSES_TYPE",
88 1033: "CONSTRAINT_REQUIRED",
89 1034: "BIT_INFO",
90 1035: "NOSPEC",
91 1036: "NOSPEC_WB",
92 1037: "STMT_CNT",
93 1038: "TERMINATED",
94 1039: "SLEEP",
95 1040: "NO_SLEEP_CNT",
96 1041: "SMALLISH",
97 1042: "FRESH_MTAG",
98
99 8017: "USER_DATA",
100 9017: "USER_DATA_SET",
101 8018: "NO_OVERFLOW",
102 8019: "NO_OVERFLOW_SIMPLE",
103 8020: "LOCKED",
104 8021: "UNLOCKED",
105 8023: "ATOMIC_INC",
106 8024: "ATOMIC_DEC",
107 };
108
109 def add_range(rl, min_val, max_val):
110 check_next = 0
111 done = 0
112 ret = []
113 idx = 0
114
115 if len(rl) == 0:
116 return [[min_val, max_val]]
117
118 for idx in range(len(rl)):
119 cur_min = rl[idx][0]
120 cur_max = rl[idx][1]
121
122 # we already merged the new range but we might need to change later
123 # ranges if they over lap with more than one
124 if check_next:
125 # join with added range
126 if max_val + 1 == cur_min:
127 ret[len(ret) - 1][1] = cur_max
128 done = 1
129 break
130 # don't overlap
131 if max_val < cur_min:
132 ret.append([cur_min, cur_max])
133 done = 1
134 break
135 # partially overlap
136 if max_val < cur_max:
137 ret[len(ret) - 1][1] = cur_max
138 done = 1
139 break
140 # completely overlap
141 continue
142
143 # join 2 ranges into one
144 if max_val + 1 == cur_min:
145 ret.append([min_val, cur_max])
146 done = 1
147 break
148 # range is entirely below
149 if max_val < cur_min:
150 ret.append([min_val, max_val])
151 ret.append([cur_min, cur_max])
152 done = 1
153 break
154 # range is partially below
155 if min_val < cur_min:
156 if max_val <= cur_max:
157 ret.append([min_val, cur_max])
158 done = 1
159 break
160 else:
161 ret.append([min_val, max_val])
162 check_next = 1
163 continue
164 # range already included
165 if max_val <= cur_max:
166 ret.append([cur_min, cur_max])
167 done = 1
168 break;
169 # range partially above
170 if min_val <= cur_max:
171 ret.append([cur_min, max_val])
172 check_next = 1
173 continue
174 # join 2 ranges on the other side
175 if min_val - 1 == cur_max:
176 ret.append([cur_min, max_val])
177 check_next = 1
178 continue
179 # range is above
180 ret.append([cur_min, cur_max])
181
182 if idx + 1 < len(rl): # we hit a break statement
183 ret = ret + rl[idx + 1:]
184 elif done: # we hit a break on the last iteration
185 pass
186 elif not check_next: # it's past the end of the rl
187 ret.append([min_val, max_val])
188
189 return ret;
190
191 def rl_union(rl1, rl2):
192 ret = []
193 for r in rl1:
194 ret = add_range(ret, r[0], r[1])
195 for r in rl2:
196 ret = add_range(ret, r[0], r[1])
197
198 if (rl1 or rl2) and not ret:
199 print "bug: merging %s + %s gives empty" %(rl1, rl2)
200
201 return ret
202
203 def txt_to_val(txt):
204 if txt == "s64min":
205 return -(2**63)
206 elif txt == "s32min":
207 return -(2**31)
208 elif txt == "s16min":
209 return -(2**15)
210 elif txt == "s64max":
211 return 2**63 - 1
212 elif txt == "s32max":
213 return 2**31 - 1
214 elif txt == "s16max":
215 return 2**15 - 1
216 elif txt == "u64max":
217 return 2**64 - 1
218 elif txt == "ptr_max":
219 return 2**64 - 1
220 elif txt == "u32max":
221 return 2**32 - 1
222 elif txt == "u16max":
223 return 2**16 - 1
224 else:
225 try:
226 return int(txt)
227 except ValueError:
228 return 0
229
230 def val_to_txt(val):
231 if val == -(2**63):
232 return "s64min"
233 elif val == -(2**31):
234 return "s32min"
235 elif val == -(2**15):
236 return "s16min"
237 elif val == 2**63 - 1:
238 return "s64max"
239 elif val == 2**31 - 1:
240 return "s32max"
241 elif val == 2**15 - 1:
242 return "s16max"
243 elif val == 2**64 - 1:
244 return "u64max"
245 elif val == 2**32 - 1:
246 return "u32max"
247 elif val == 2**16 - 1:
248 return "u16max"
249 elif val < 0:
250 return "(%d)" %(val)
251 else:
252 return "%d" %(val)
253
254 def get_next_str(txt):
255 val = ""
256 parsed = 0
257
258 if txt[0] == '(':
259 parsed += 1
260 for char in txt[1:]:
261 if char == ')':
262 break
263 parsed += 1
264 val = txt[1:parsed]
265 parsed += 1
266 elif txt[0] == 's' or txt[0] == 'u':
267 parsed += 6
268 val = txt[:parsed]
269 else:
270 if txt[0] == '-':
271 parsed += 1
272 for char in txt[parsed:]:
273 if char == '-' or char == '[':
274 break
275 parsed += 1
276 val = txt[:parsed]
277 return [parsed, val]
278
279 def txt_to_rl(txt):
280 if len(txt) == 0:
281 return []
282
283 ret = []
284 pairs = txt.split(",")
285 for pair in pairs:
286 cnt, min_str = get_next_str(pair)
287 if cnt == len(pair):
288 max_str = min_str
289 else:
290 cnt, max_str = get_next_str(pair[cnt + 1:])
291 min_val = txt_to_val(min_str)
292 max_val = txt_to_val(max_str)
293 ret.append([min_val, max_val])
294
295 # Hm... Smatch won't call INT_MAX s32max if the variable is unsigned.
296 # if txt != rl_to_txt(ret):
297 # print "bug: converting: text = %s rl = %s internal = %s" %(txt, rl_to_txt(ret), ret)
298
299 return ret
300
301 def rl_to_txt(rl):
302 ret = ""
303 for idx in range(len(rl)):
304 cur_min = rl[idx][0]
305 cur_max = rl[idx][1]
306
307 if idx != 0:
308 ret += ","
309
310 if cur_min == cur_max:
311 ret += val_to_txt(cur_min)
312 else:
313 ret += val_to_txt(cur_min)
314 ret += "-"
315 ret += val_to_txt(cur_max)
316 return ret
317
318 def type_to_str(type_int):
319
320 t = int(type_int)
321 if db_types.has_key(t):
322 return db_types[t]
323 return type_int
324
325 def type_to_int(type_string):
326 for k in db_types.keys():
327 if db_types[k] == type_string:
328 return k
329 return -1
330
331 def display_caller_info(printed, cur, param_names):
332 for txt in cur:
333 if not printed:
334 print "file | caller | function | type | parameter | key | value |"
335 printed = 1
336
337 parameter = int(txt[6])
338 key = txt[7]
339 if len(param_names) and parameter in param_names:
340 key = key.replace("$", param_names[parameter])
341
342 print "%20s | %20s | %20s |" %(txt[0], txt[1], txt[2]),
343 print " %10s |" %(type_to_str(txt[5])),
344 print " %d | %s | %s" %(parameter, key, txt[8])
345 return printed
346
347 def get_caller_info(filename, ptrs, my_type):
348 cur = con.cursor()
349 param_names = get_param_names(filename, func)
350 printed = 0
351 type_filter = ""
352 if my_type != "":
353 type_filter = "and type = %d" %(type_to_int(my_type))
354 for ptr in ptrs:
355 cur.execute("select * from caller_info where function = '%s' %s;" %(ptr, type_filter))
356 printed = display_caller_info(printed, cur, param_names)
357
358 def print_caller_info(filename, func, my_type = ""):
359 ptrs = get_function_pointers(func)
360 get_caller_info(filename, ptrs, my_type)
361
362 def merge_values(param_names, vals, cur):
363 for txt in cur:
364 parameter = int(txt[0])
365 name = txt[1]
366 rl = txt_to_rl(txt[2])
367 if parameter in param_names:
368 name = name.replace("$", param_names[parameter])
369
370 if not parameter in vals:
371 vals[parameter] = {}
372
373 # the first item on the list is the number of rows. it's incremented
374 # every time we call merge_values().
375 if name in vals[parameter]:
376 vals[parameter][name] = [vals[parameter][name][0] + 1, rl_union(vals[parameter][name][1], rl)]
377 else:
378 vals[parameter][name] = [1, rl]
379
380 def get_param_names(filename, func):
381 cur = con.cursor()
382 param_names = {}
383 cur.execute("select parameter, value from parameter_name where file = '%s' and function = '%s';" %(filename, func))
384 for txt in cur:
385 parameter = int(txt[0])
386 name = txt[1]
387 param_names[parameter] = name
388 if len(param_names):
389 return param_names
390
391 cur.execute("select parameter, value from parameter_name where function = '%s';" %(func))
392 for txt in cur:
393 parameter = int(txt[0])
394 name = txt[1]
395 param_names[parameter] = name
396 return param_names
397
398 def get_caller_count(ptrs):
399 cur = con.cursor()
400 count = 0
401 for ptr in ptrs:
402 cur.execute("select count(distinct(call_id)) from caller_info where function = '%s';" %(ptr))
403 for txt in cur:
404 count += int(txt[0])
405 return count
406
407 def print_merged_caller_values(filename, func, ptrs, param_names, call_cnt):
408 cur = con.cursor()
409 vals = {}
410 for ptr in ptrs:
411 cur.execute("select parameter, key, value from caller_info where function = '%s' and type = %d;" %(ptr, type_to_int("PARAM_VALUE")))
412 merge_values(param_names, vals, cur);
413
414 for param in sorted(vals):
415 for name in sorted(vals[param]):
416 if vals[param][name][0] != call_cnt:
417 continue
418 print "%d %s -> %s" %(param, name, rl_to_txt(vals[param][name][1]))
419
420
421 def print_unmerged_caller_values(filename, func, ptrs, param_names):
422 cur = con.cursor()
423 for ptr in ptrs:
424 prev = -1
425 cur.execute("select file, caller, call_id, parameter, key, value from caller_info where function = '%s' and type = %d;" %(ptr, type_to_int("PARAM_VALUE")))
426 for filename, caller, call_id, parameter, name, value in cur:
427 if prev != int(call_id):
428 prev = int(call_id)
429
430 parameter = int(parameter)
431 if parameter < len(param_names):
432 name = name.replace("$", param_names[parameter])
433 else:
434 name = name.replace("$", "$%d" %(parameter))
435
436 print "%s | %s | %s | %s" %(filename, caller, name, value)
437 print "=========================="
438
439 def print_caller_values(filename, func, ptrs):
440 param_names = get_param_names(filename, func)
441 call_cnt = get_caller_count(ptrs)
442
443 print_merged_caller_values(filename, func, ptrs, param_names, call_cnt)
444 print "=========================="
445 print_unmerged_caller_values(filename, func, ptrs, param_names)
446
447 def caller_info_values(filename, func):
448 ptrs = get_function_pointers(func)
449 print_caller_values(filename, func, ptrs)
450
451 def print_return_states(func):
452 cur = con.cursor()
453 cur.execute("select * from return_states where function = '%s';" %(func))
454 count = 0
455 for txt in cur:
456 printed = 1
457 if count == 0:
458 print "file | function | return_id | return_value | type | param | key | value |"
459 count += 1
460 print "%s | %s | %2s | %13s" %(txt[0], txt[1], txt[3], txt[4]),
461 print "| %13s |" %(type_to_str(txt[6])),
462 print " %2d | %20s | %20s |" %(txt[7], txt[8], txt[9])
463
464 def print_return_implies(func):
465 cur = con.cursor()
466 cur.execute("select * from return_implies where function = '%s';" %(func))
467 count = 0
468 for txt in cur:
469 if not count:
470 print "file | function | type | param | key | value |"
471 count += 1
472 print "%15s | %15s" %(txt[0], txt[1]),
473 print "| %15s" %(type_to_str(txt[4])),
474 print "| %3d | %s | %15s |" %(txt[5], txt[6], txt[7])
475
476 def print_type_size(struct_type, member):
477 cur = con.cursor()
478 cur.execute("select * from type_size where type like '(struct %s)->%s';" %(struct_type, member))
479 print "type | size"
480 for txt in cur:
481 print "%-15s | %s" %(txt[0], txt[1])
482
483 cur.execute("select * from function_type_size where type like '(struct %s)->%s';" %(struct_type, member))
484 print "file | function | type | size"
485 for txt in cur:
486 print "%-15s | %-15s | %-15s | %s" %(txt[0], txt[1], txt[2], txt[3])
487
488 def print_data_info(struct_type, member):
489 cur = con.cursor()
490 cur.execute("select * from data_info where data like '(struct %s)->%s';" %(struct_type, member))
491 print "file | data | type | value"
492 for txt in cur:
493 print "%-15s | %-15s | %-15s | %s" %(txt[0], txt[1], type_to_str(txt[2]), txt[3])
494
495 def print_fn_ptrs(func):
496 ptrs = get_function_pointers(func)
497 if not ptrs:
498 return
499 print "%s = " %(func),
500 print(ptrs)
501
502 def print_functions(member):
503 cur = con.cursor()
504 cur.execute("select * from function_ptr where ptr like '%%->%s';" %(member))
505 print "File | Pointer | Function | Static"
506 for txt in cur:
507 print "%-15s | %-15s | %-15s | %s" %(txt[0], txt[2], txt[1], txt[3])
508
509 def get_callers(func):
510 ret = []
511 cur = con.cursor()
512 ptrs = get_function_pointers(func)
513 for ptr in ptrs:
514 cur.execute("select distinct caller from caller_info where function = '%s';" %(ptr))
515 for row in cur:
516 ret.append(row[0])
517 return ret
518
519 printed_funcs = []
520 def call_tree_helper(func, indent = 0):
521 global printed_funcs
522 if func in printed_funcs:
523 return
524 print "%s%s()" %(" " * indent, func)
525 if func == "too common":
526 return
527 if indent > 6:
528 return
529 printed_funcs.append(func)
530 callers = get_callers(func)
531 if len(callers) >= 20:
532 print "Over 20 callers for %s()" %(func)
533 return
534 for caller in callers:
535 call_tree_helper(caller, indent + 2)
536
537 def print_call_tree(func):
538 global printed_funcs
539 printed_funcs = []
540 call_tree_helper(func)
541
542 def function_type_value(struct_type, member):
543 cur = con.cursor()
544 cur.execute("select * from function_type_value where type like '(struct %s)->%s';" %(struct_type, member))
545 for txt in cur:
546 print "%-30s | %-30s | %s | %s" %(txt[0], txt[1], txt[2], txt[3])
547
548 def rl_too_big(txt):
549 rl = txt_to_rl(txt)
550 ret = ""
551 for idx in range(len(rl)):
552 cur_max = rl[idx][1]
553 if (cur_max > 0xFFFFFFFFFFFFFF):
554 return 1
555
556 return 0
557
558 def rl_has_min_untagged(txt):
559 rl = txt_to_rl(txt)
560 ret = ""
561 for idx in range(len(rl)):
562 cur_min = rl[idx][0]
563 if (cur_min == 0xff80000000000000):
564 return 1
565
566 return 0
567
568 def rl_is_tagged(txt):
569 if not rl_too_big(txt):
570 return 0
571
572 if rl_has_min_untagged(txt):
573 return 0
574
575 return 1
576
577 def rl_is_treat_untagged(txt):
578 if "[u]" in txt:
579 return 1;
580
581 return 0
582
583 def parse_warns_tagged(filename):
584 proc = subprocess.Popen(['cat %s | grep "potentially tagged" | sort | uniq' %(filename)], shell=True, stdout=subprocess.PIPE)
585 while True:
586 line = proc.stdout.readline()
587 if not line:
588 break
589
590 linepos = re.search("([^\s]+)", line).group(1)
591 groupre = re.search("potentially tagged address \(([^,]+), ([^,]+), ([^\)]+)\)", line)
592 groupre.group(1)
593
594 func = groupre.group(1)
595 param = int(groupre.group(2))
596 var = groupre.group(3)
597
598 if ("end" in var or "size" in var or "len" in var):
599 continue
600
601 print "\n%s (func: %s, param: %d:%s) may be caused by:" %(linepos, func, param, var)
602
603 if (param != -1):
604 if not find_tagged(func, param, 0, []):
605 print " %s (param %d) (can't walk call tree)" % (func, param)
606 else:
607 print " %s (variable %s (can't walk call tree)" % (func, var)
608
609 def find_tagged(func, param, caller_call_id, printed):
610
611 callers = {}
612 cur = con.cursor()
613 ptrs = get_function_pointers(func)
614 found = 0
615
616 for ptr in ptrs:
617 cur.execute("select call_id, value from caller_info where function = '%s' and parameter=%d and type=%d" %(ptr, param, type_to_int("DATA_SOURCE")))
618
619 for row in cur:
620 if (row[1][0] == '$'):
621 if row[0] not in callers:
622 callers[row[0]] = {}
623 callers[row[0]]["param"] = int(row[1][1])
624
625 for ptr in ptrs:
626 cur.execute("select caller, call_id, value from caller_info where function = '%s' and parameter=%d and type=%d" %(ptr, param, type_to_int("USER_DATA")))
627
628 for row in cur:
629 if not rl_is_tagged(row[2]):
630 continue
631 if rl_is_treat_untagged(row[2]):
632 continue
633 found = 1
634 if row[1] not in callers:
635 callers[row[1]] = {}
636 if "param" not in callers[row[1]]:
637 line = " %s (param ?) -> %s (param %d)" % (row[0], func, param)
638 if line not in printed:
639 printed.append(line)
640 print line
641 continue
642 if row[0] not in printed:
643 printed.append(row[0])
644 if not find_tagged(row[0], callers[row[1]]["param"], row[1], printed):
645 print " %s (param %d)" % (row[0], param)
646
647 return found
648
649 def trace_callers(func, param):
650 sources = []
651 prev_type = 0
652
653 cur = con.cursor()
654 ptrs = get_function_pointers(func)
655 for ptr in ptrs:
656 cur.execute("select type, caller, value from caller_info where function = '%s' and (type = 0 or type = 1014 or type = 1028) and (parameter = -1 or parameter = %d);" %(ptr, param))
657 for row in cur:
658 data_type = int(row[0])
659 if data_type == 1014:
660 sources.append((row[1], row[2]))
661 elif data_type == 1028:
662 sources.append(("%", row[2])) # hack...
663 elif data_type == 0 and prev_type == 0:
664 sources.append((row[1], ""))
665 prev_type = data_type
666 return sources
667
668 def trace_param_helper(func, param, indent = 0):
669 global printed_funcs
670 if func in printed_funcs:
671 return
672 print "%s%s(param %d)" %(" " * indent, func, param)
673 if func == "too common":
674 return
675 if indent > 20:
676 return
677 printed_funcs.append(func)
678 sources = trace_callers(func, param)
679 for path in sources:
680
681 if len(path[1]) and path[1][0] == '$':
682 p = int(re.findall('\d+', path[1][1:])[0])
683 trace_param_helper(path[0], p, indent + 2)
684 elif len(path[0]) and path[0][0] == '%':
685 print " %s%s" %(" " * indent, path[1])
686 else:
687 print "* %s%s %s" %(" " * (indent - 1), path[0], path[1])
688
689 def trace_param(func, param):
690 global printed_funcs
691 printed_funcs = []
692 print "tracing %s %d" %(func, param)
693 trace_param_helper(func, param)
694
695 def print_locals(filename):
696 cur = con.cursor()
697 cur.execute("select file,data,value from data_info where file = '%s' and type = 8029 and value != 0;" %(filename))
698 for txt in cur:
699 print "%s | %s | %s" %(txt[0], txt[1], txt[2])
700
701 def constraint(struct_type, member):
702 cur = con.cursor()
703 cur.execute("select * from constraints_required where data like '(struct %s)->%s' or bound like '(struct %s)->%s';" %(struct_type, member, struct_type, member))
704 for txt in cur:
705 print "%-30s | %-30s | %s | %s" %(txt[0], txt[1], txt[2], txt[3])
706
707 if len(sys.argv) < 2:
708 usage()
709
710 if len(sys.argv) == 2:
711 func = sys.argv[1]
712 print_caller_info("", func)
713 elif sys.argv[1] == "info":
714 my_type = ""
715 if len(sys.argv) == 4:
716 my_type = sys.argv[3]
717 func = sys.argv[2]
718 print_caller_info("", func, my_type)
719 elif sys.argv[1] == "call_info":
720 if len(sys.argv) != 4:
721 usage()
722 filename = sys.argv[2]
723 func = sys.argv[3]
724 caller_info_values(filename, func)
725 print_caller_info(filename, func)
726 elif sys.argv[1] == "function_ptr" or sys.argv[1] == "fn_ptr":
727 func = sys.argv[2]
728 print_fn_ptrs(func)
729 elif sys.argv[1] == "return_states":
730 func = sys.argv[2]
731 print_return_states(func)
732 print "================================================"
733 print_return_implies(func)
734 elif sys.argv[1] == "return_implies":
735 func = sys.argv[2]
736 print_return_implies(func)
737 elif sys.argv[1] == "type_size" or sys.argv[1] == "buf_size":
738 struct_type = sys.argv[2]
739 member = sys.argv[3]
740 print_type_size(struct_type, member)
741 elif sys.argv[1] == "data_info":
742 struct_type = sys.argv[2]
743 member = sys.argv[3]
744 print_data_info(struct_type, member)
745 elif sys.argv[1] == "call_tree":
746 func = sys.argv[2]
747 print_call_tree(func)
748 elif sys.argv[1] == "find_tagged":
749 func = sys.argv[2]
750 param = int(sys.argv[3])
751 find_tagged(func, param, 0, [])
752 elif sys.argv[1] == "parse_warns_tagged":
753 filename = sys.argv[2]
754 parse_warns_tagged(filename)
755 elif sys.argv[1] == "where":
756 if len(sys.argv) == 3:
757 struct_type = "%"
758 member = sys.argv[2]
759 elif len(sys.argv) == 4:
760 struct_type = sys.argv[2]
761 member = sys.argv[3]
762 function_type_value(struct_type, member)
763 elif sys.argv[1] == "local":
764 filename = sys.argv[2]
765 variable = ""
766 if len(sys.argv) == 4:
767 variable = sys.argv[3]
768 local_values(filename, variable)
769 elif sys.argv[1] == "functions":
770 member = sys.argv[2]
771 print_functions(member)
772 elif sys.argv[1] == "trace_param":
773 if len(sys.argv) != 4:
774 usage()
775 func = sys.argv[2]
776 param = int(sys.argv[3])
777 trace_param(func, param)
778 elif sys.argv[1] == "locals":
779 if len(sys.argv) != 3:
780 usage()
781 filename = sys.argv[2]
782 print_locals(filename);
783 elif sys.argv[1] == "constraint":
784 if len(sys.argv) == 3:
785 struct_type = "%"
786 member = sys.argv[2]
787 elif len(sys.argv) == 4:
788 struct_type = sys.argv[2]
789 member = sys.argv[3]
790 constraint(struct_type, member)
791 else:
792 usage()