| import os |
| import sys |
| import subprocess |
| import re |
| from threading import Timer |
| |
| global_front = """ |
| #include "debug-snapshot-log.h" |
| |
| struct dbg_snapshot_log *p; |
| |
| void remove_non_ascii(char * s, int len) { |
| for (int i = 0; i < len; i++) { |
| if(!s[i]) break; |
| if(!(32 <= s[i] && s[i] < 127) || s[i] == '\\'') s[i] = ' '; |
| } |
| s[len-1] = 0; |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| FILE *f; |
| int ch; |
| long fsize; |
| char *string; |
| int i0, i1, i2; |
| |
| f = fopen(argv[1], "rb"); |
| if (f == NULL) { |
| fputs("file read error!", stderr); |
| exit(1); |
| } |
| fseek(f, 0, SEEK_END); |
| fsize = ftell(f); |
| fseek(f, 0, SEEK_SET); //same as rewind(f); |
| |
| string = malloc(fsize + 1); |
| fread(string, fsize, 1, f); |
| fclose(f); |
| p = (struct dbg_snapshot_log *)string; |
| |
| printf("log = []\\n"); |
| """ |
| |
| global_back = """ |
| return 0; |
| } |
| """ |
| |
| |
| ### Below classes substitute global variable. |
| class Singleton(type): |
| _instances = {} |
| def __call__(cls, *args, **kwargs): |
| if cls not in cls._instances: |
| cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) |
| return cls._instances[cls] |
| |
| def execget(cmd, to=100, printerr=False): |
| def kill(process): |
| process.kill(); |
| stdout = "" |
| stderr = "" |
| p = subprocess.Popen(cmd, shell="True", stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=os.environ); |
| my_timer = Timer(to, kill, [p]); |
| try: |
| my_timer.start() |
| stdout, stderr = p.communicate() |
| finally: |
| my_timer.cancel() |
| return stdout; |
| |
| def expect(current, expect, short_msg): |
| if current not in expect: |
| msg = """ |
| Error : %s |
| Current output: %s |
| Expect output : %s |
| """ % (short_msg, current, expect) |
| raise Exception(msg) |
| |
| class TypeTable: |
| __metaclass__ = Singleton |
| def __init__(self): |
| if "data" in self.__dict__: |
| return |
| self.data = {} |
| def get(self, key): |
| return self.data.get(key, None) |
| def set(self, key, value): |
| self.data[key] = value |
| def clear(self): |
| self.data = {} |
| def __repr__(self): |
| ret = "" |
| for k, v in self.data.items(): |
| ret += "%s\n" % str(v) |
| return ret |
| |
| class FieldTable: |
| __metaclass__ = Singleton |
| def __init__(self): |
| if "d" in self.__dict__: |
| return |
| self.d = [] |
| def find_typedecl_all(self): |
| for x in self.d: |
| x.find_typedecl() |
| ### Primitive Types ----------------------------- |
| |
| class Decl: |
| @staticmethod |
| def find_depth(line): |
| s = 0 |
| while not line[s].isalpha(): |
| s += 1 |
| if s < len(line): |
| continue |
| s = -1; |
| break |
| return s |
| |
| class TypeDecl(Decl): |
| def __init__(self, name, abv): |
| self.name = name |
| self.abv = abv |
| typetab = TypeTable() |
| typetab.set(self.name, self) |
| |
| def __repr__(self): |
| return self.name |
| |
| @staticmethod |
| def add_primative(): |
| TypeDecl("short", "%hd") |
| TypeDecl("unsigned short", "%hu") |
| TypeDecl("signed short", "%hd") |
| |
| TypeDecl("int", "%d") |
| TypeDecl("unsigned int", "%u") |
| TypeDecl("signed int", "%d") |
| |
| TypeDecl("unsigned char", "%u") |
| TypeDecl("signed char", "%d") |
| |
| TypeDecl("long", "%ld") |
| TypeDecl("unsigned long", "%lu") |
| TypeDecl("signed long", "%lu") |
| |
| TypeDecl("long long", "%lld") |
| TypeDecl("unsigned long long", "%llu") |
| TypeDecl("signed long long", "%lld") |
| |
| TypeDecl("double", "%lf") |
| TypeDecl("float", "%f") |
| |
| TypeDecl("pointer", "'%p'") |
| TypeDecl("char pointer", "'%p'") |
| |
| TypeDecl("char", "'%s'") |
| |
| TypeDecl("unknown", "") |
| |
| ### RecordDecl Types ----------------------------- |
| |
| class RecordDecl(TypeDecl): |
| def __init__(self, line): |
| self.depth = Decl.find_depth(line) |
| line = line[self.depth:] |
| arr = line.strip().split(" ") |
| self.id = arr[1] |
| self.name = arr[-2] |
| self.field = [] |
| typetab = TypeTable() |
| typetab.set(self.name, self) |
| |
| def __repr__(self): |
| ret = "%s %s\n" % (self.id, self.name) |
| for x in self.field: |
| ret += " %s\n" % str(x) |
| return ret |
| |
| ### FieldDecl Types ----------------------------- |
| |
| """ |
| |-RecordDecl 0x5c53f80 parent 0x5aad4f8 <line:353:2, line:358:2> line:353:9 struct __printk_log definition |
| | |-FieldDecl 0x5c54058 <line:354:3, col:22> col:22 time 'unsigned long long' |
| | |-FieldDecl 0x5c540b8 <line:355:3, col:7> col:7 cpu 'int' |
| | |-FieldDecl 0x5c54178 <line:356:3, col:33> col:8 log 'char [128]' |
| | `-FieldDecl 0x5c54220 <line:357:3, col:37> col:9 caller 'void *[4]' |
| """ |
| |
| class FieldDecl(Decl): |
| def __init__(self, line, record): |
| self.record = record |
| self.depth = Decl.find_depth(line) |
| line = line[self.depth:] |
| arr = line.strip().split(" ", 6) |
| self.id = arr[1] |
| self.name = arr[-2] |
| if "':'" in arr[-1]: |
| self.type = arr[-1].split("':'")[-1].strip().strip("'") |
| else: |
| self.type = arr[-1].strip().strip("'") |
| array = re.findall(r"\[(?P<index>\d+)\]", self.type) |
| self.array = [int(i) for i in array] |
| for n in self.array: |
| self.type = self.type.replace("[%d]" % n ,"").strip() |
| fieldtab = FieldTable() |
| fieldtab.d.append(self) |
| |
| def find_typedecl(self): |
| typetab = TypeTable() |
| self.type = self.type.replace("struct ", "") |
| self.type = self.type.replace("union ", "") |
| if "*" in self.type and "char" in self.type: |
| self.type = "char pointer" |
| if "*" in self.type: |
| self.type = "pointer" |
| if typetab.get(self.type): |
| self.type = typetab.get(self.type) |
| else: |
| self.type = typetab.get("unknown") |
| |
| def __repr__(self): |
| ret = "%s %s " % (self.id, self.name) |
| array = "" |
| for n in self.array: |
| array += "[%d]" % n |
| ret += "'%s %s'" % (str(self.type), array) |
| return ret |
| |
| def array_printer(self, array): |
| front_array = "[" |
| for i in range(self.array[0]): |
| front_array += "%s, " % self.type.abv |
| front_array = front_array[:-1] + "]" |
| |
| mid = "" |
| for i in range(self.array[0]): |
| if self.type.name == "pointer": |
| mid += "p->%s.%s[%d] ? p->%s.%s[%d] :(void**)-1,\n" %(array, self.name, i, array, self.name, i) |
| else: |
| mid += "p->%s.%s[%d],\n" %(array, self.name, i) |
| |
| front = "'%s' :%s, " % (self.name, front_array) |
| return front, mid |
| |
| def printer(self, printf_obj=None): |
| ## Check printer is called by root. |
| if not printf_obj: |
| pf = Printf(self.array, self.name) |
| else: |
| pf = printf_obj |
| |
| ## Check this type is primitvie |
| if hasattr(self.type, "abv"): |
| pf.add_field(self, pf.ref_name) |
| else: |
| ## structs are layered like Binder, self.name is cascaded to ref_name |
| if printf_obj: |
| temp = pf.ref_name |
| pf.ref_name = "%s.%s" % (pf.ref_name, self.name) |
| for f in self.type.field: |
| f.printer(pf) |
| if printf_obj: |
| pf.ref_name = temp |
| |
| ## If printer is succeed in root, save all fields as string |
| if not printf_obj: |
| return pf.save_all() |
| return None |
| |
| ### Printf class -------------------------------- |
| |
| class Printf: |
| def __init__(self, bounds, name): |
| self.bounds = bounds |
| self.name = name |
| self.ref_name = name |
| |
| ## Arrays for storing fields |
| self.fields = [] |
| self.pointer_fields = [] |
| self.string_fields = [] |
| self.array_fields = [] |
| self.pointer_array_fields = [] |
| |
| ## Result strings will be concatenated right before returing. |
| self.for_statement = "" |
| self.for_statement_back = "" |
| self.for_body = "" |
| self.printf_front = "" |
| self.printf_format = "" |
| self.printf_mid = "" |
| self.printf_args = "" |
| self.printf_back = "" |
| |
| ## Makeup default string for 'for statement' & 'printf' |
| self.make_for_statement() |
| self.prepare_printf() |
| |
| ## Field is pushed from outside by this method |
| def add_field(self, x, ref_name): |
| x.ref_name = ref_name |
| if x.name == "time" : |
| pass |
| elif x.type.name == "char": |
| self.string_fields.append(x) |
| elif len(x.array) > 0 and x.type.name != "pointer" : |
| self.array_fields.append(x) |
| elif len(x.array) > 0 and x.type.name == "pointer" : |
| self.pointer_array_fields.append(x) |
| elif x.type.name == "pointer": |
| self.pointer_fields.append(x) |
| else: |
| self.fields.append(x) |
| |
| def make_for_statement(self): |
| for index, n in enumerate(self.bounds): |
| self.for_statement += "for (i%d = 0; i%d < %d; i%d++) {\n" % (index, index, n, index) |
| self.for_statement_back += "}\n" |
| self.ref_name += "[i%d]" % index |
| self.for_body += "if (p->%s.time == 0) break;\n" % (self.ref_name) |
| |
| def prepare_printf(self): |
| self.printf_front += "printf(\"log.append({ 'type' : '%s', " % self.name |
| self.printf_mid += "})\\n\",\n" |
| self.printf_back += ");\n" |
| |
| self.printf_format += "'time' : %lld.%09lld, " |
| self.printf_args += "p->%s.time/1000000000LL," % self.ref_name |
| self.printf_args += "p->%s.time%%1000000000LL,\n" % self.ref_name |
| if len(self.bounds) > 1 and self.bounds[0] == 8: |
| self.printf_format += "'cpu' : %d, " |
| self.printf_args += "i0,\n" |
| |
| ## Iterate all fields, and Convert it to C-code |
| def process_fields(self): |
| for x in self.fields: |
| self.print_normal(x) |
| for x in self.string_fields: |
| self.print_string(x) |
| for x in self.array_fields: |
| self.print_array(x) |
| for x in self.pointer_array_fields: |
| self.print_pointer_array(x) |
| for x in self.pointer_fields: |
| self.print_pointer(x) |
| |
| def print_normal(self, x): |
| self.printf_format += "'%s' : %s, " % (x.name, x.type.abv) |
| self.printf_args += "p->%s.%s,\n" %(x.ref_name, x.name) |
| |
| def print_pointer(self, x): |
| self.printf_format += "'%s' : %s, " % (x.name, x.type.abv) |
| self.printf_args += "p->%s.%s ? p->%s.%s :(void**)-1,\n" %(x.ref_name, x.name, x.ref_name, x.name) |
| |
| def print_string(self, x): |
| self.printf_format += "'%s' : %s, " % (x.name, x.type.abv) |
| self.printf_args += "p->%s.%s,\n" %(x.ref_name, x.name) |
| self.for_body += ("remove_non_ascii(p->%s.%s, %d);\n" %(x.ref_name, x.name, x.array[0])) |
| |
| def print_array(self, x): |
| front_array = "[" |
| for i in range(x.array[0]): |
| front_array += "%s, " % x.type.abv |
| front_array = front_array[:-1] + "]" |
| for i in range(x.array[0]): |
| self.printf_args += "p->%s.%s[%d],\n" %(x.ref_name, x.name, i) |
| self.printf_format += "'%s' :%s, " % (x.name, front_array) |
| |
| def print_pointer_array(self, x): |
| front_array = "[" |
| for i in range(x.array[0]): |
| front_array += "%s, " % x.type.abv |
| front_array = front_array[:-1] + "]" |
| for i in range(x.array[0]): |
| self.printf_args += "p->%s.%s[%d] ? p->%s.%s[%d] :(void**)-1,\n" %(x.ref_name, x.name, i, x.ref_name, x.name, i) |
| self.printf_format += "'%s' :%s, " % (x.name, front_array) |
| |
| def save_all(self): |
| self.process_fields() |
| ret = "" |
| ret += self.for_statement |
| ret += self.for_body |
| ret += self.printf_front |
| ret += self.printf_format |
| ret += self.printf_mid |
| ret += self.printf_args[:-2] |
| ret += self.printf_back |
| ret += self.for_statement_back |
| return ret |
| |
| ### ASTdump Class ----------------------------- |
| |
| class ASTdump: |
| def __init__(self): |
| if "clang" in os.environ["CC"]: |
| self.clang = os.environ["CC"] |
| elif "clang" in execget("which clang"): |
| self.clang = "clang" |
| |
| def run_inner(self, i, record): |
| depth = record.depth |
| ret = [] |
| for line in self.dump[i+1:]: |
| inner_depth = Decl.find_depth(line) |
| if depth >= inner_depth: |
| break |
| if depth + 2 != inner_depth: |
| continue |
| if not "FieldDecl" in line: |
| continue |
| ret.append(FieldDecl(line, record)) |
| return ret |
| |
| def prepare(self): |
| self.dump = execget("%s -Iinclude -Xclang -ast-dump -fsyntax-only -DDSS_ANALYZER lib/debug-snapshot-log.h" % self.clang) |
| self.dump = self.dump.strip().split("\n") |
| for i, line in enumerate(self.dump): |
| if not "RecordDecl" in line: |
| continue |
| record = RecordDecl(line) |
| record.field = self.run_inner(i, record) |
| FieldTable().find_typedecl_all() |
| |
| def run(self): |
| main_root = TypeTable().get("dbg_snapshot_log") |
| ret = "" |
| for r in main_root.field: |
| ret += r.printer() |
| return global_front + ret + global_back |
| |
| if __name__ == "__main__": |
| TypeDecl.add_primative() |
| astdump = ASTdump() |
| astdump.prepare() |
| ret = astdump.run() |
| print(ret) |
| |