xref: /illumos-gate/usr/src/tools/smatch/src/smatch_data/db/smdb.py (revision f52943a93040563107b95bccb9db87d9971ef47d)
1#!/usr/bin/python
2
3# Copyright (C) 2013 Oracle.
4#
5# Licensed under the Open Software License version 1.1
6
7import sqlite3
8import sys
9import re
10import subprocess
11
12try:
13    con = sqlite3.connect('smatch_db.sqlite')
14except sqlite3.Error, e:
15    print "Error %s:" % e.args[0]
16    sys.exit(1)
17
18def 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
34function_ptrs = []
35searched_ptrs = []
36def 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
48def 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
56db_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
109def 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
191def 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
203def 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
230def 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
254def 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
279def 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
301def 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
318def 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
325def 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
331def 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
347def 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
358def print_caller_info(filename, func, my_type = ""):
359    ptrs = get_function_pointers(func)
360    get_caller_info(filename, ptrs, my_type)
361
362def 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
380def 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
398def 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
407def 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
421def 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
439def 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
447def caller_info_values(filename, func):
448    ptrs = get_function_pointers(func)
449    print_caller_values(filename, func, ptrs)
450
451def 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
464def 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
476def 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
488def 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
495def print_fn_ptrs(func):
496    ptrs = get_function_pointers(func)
497    if not ptrs:
498        return
499    print "%s = " %(func),
500    print(ptrs)
501
502def 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
509def 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
519printed_funcs = []
520def 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
537def print_call_tree(func):
538    global printed_funcs
539    printed_funcs = []
540    call_tree_helper(func)
541
542def 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
548def 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
558def 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
568def 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
577def rl_is_treat_untagged(txt):
578    if "[u]" in txt:
579        return 1;
580
581    return 0
582
583def 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
609def 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
649def 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
668def 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
689def 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
695def 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
701def 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
707if len(sys.argv) < 2:
708    usage()
709
710if len(sys.argv) == 2:
711    func = sys.argv[1]
712    print_caller_info("", func)
713elif 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)
719elif 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)
726elif sys.argv[1] == "function_ptr" or sys.argv[1] == "fn_ptr":
727    func = sys.argv[2]
728    print_fn_ptrs(func)
729elif sys.argv[1] == "return_states":
730    func = sys.argv[2]
731    print_return_states(func)
732    print "================================================"
733    print_return_implies(func)
734elif sys.argv[1] == "return_implies":
735    func = sys.argv[2]
736    print_return_implies(func)
737elif 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)
741elif sys.argv[1] == "data_info":
742    struct_type = sys.argv[2]
743    member = sys.argv[3]
744    print_data_info(struct_type, member)
745elif sys.argv[1] == "call_tree":
746    func = sys.argv[2]
747    print_call_tree(func)
748elif sys.argv[1] == "find_tagged":
749    func = sys.argv[2]
750    param = int(sys.argv[3])
751    find_tagged(func, param, 0, [])
752elif sys.argv[1] == "parse_warns_tagged":
753    filename = sys.argv[2]
754    parse_warns_tagged(filename)
755elif 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)
763elif 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)
769elif sys.argv[1] == "functions":
770    member = sys.argv[2]
771    print_functions(member)
772elif 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)
778elif sys.argv[1] == "locals":
779    if len(sys.argv) != 3:
780        usage()
781    filename = sys.argv[2]
782    print_locals(filename);
783elif 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)
791else:
792    usage()
793