1# * Copyright 2016, NICTA
2# *
3# * This software may be distributed and modified according to the terms
4# of
5# * the BSD 2-Clause license. Note that NO WARRANTY is provided.
6# * See "LICENSE_BSD2.txt" for details.
7# *
8# * @TAG(NICTA_BSD)import subprocess
9import subprocess
10import elf_file
11import elf_correlate
12import elf_parser
13import chronos.emitter
14import re
15from dot_utils import *
16import call_graph_utils
17import sys
18import traceback
19import graph_refine.problem as problem
20import imm_utils
21from graph_refine.target_objects import functions, functions_by_tag
22import graph_refine.target_objects as target_objects
23import cplex
24import conflict
25
26chronos_executable = "../../chronos4.2/est"
27
28fast_path = [
29'fastpath_restore',
30'fastpath_call',
31'fastpath_reply_wait',
32'slowpath'
33]
34
35dummy_funs = [
36'idle_thread', 'halt',
37] + fast_path
38
39#funs called by boot_funs only
40boot_funs_called = [
41'strncmp'
42]
43
44
45def makeGraph(f_name,fs):
46    p = fs[f_name].as_problem(problem.Problem)
47    toGraph(p,f_name)
48
49def runExtract(program_n_flags,cwd=None):
50    if cwd:
51        p = subprocess.Popen(program_n_flags,cwd=cwd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
52    else:
53        p = subprocess.Popen(program_n_flags,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
54    out,err = p.communicate()
55    retval = p.returncode
56    return retval, out, err
57
58#given the imm, call chronos and cplex to return (wcet,chronos_out,chornos_err)
59def getWcetFromImm(imm_file_name, generateILPOnly= False):
60    print 'running chronos...'
61    ret,out,err = runExtract([chronos_executable, imm_file_name])
62    print 'chronos completed, ret: %s, err: %s\n' % (ret,err)
63    if ret:
64        print 'Chronos FAILED\n out: %s\n err: %s\n' % (out,err)
65        print 'Chronos FAILED\n'
66        return None
67    else:
68        pass
69        print 'Chronos succeeded'
70    if generateILPOnly:
71        print "ILP file generated"
72        return None
73    return  float(cplex.cplexSolve(imm_file_name+'.ilp')),out,err
74
75
76def toElfImmFun(f,dir_name,load_counts):
77    ef = elfFile()
78    elf_fun = ef.funcs[elfFile().gFunName(f)]
79    imm_fun = elf_correlate.immFunc(elf_fun,load_counts=load_counts)
80    return elf_fun, imm_fun, ef
81
82def toImmFun(f,dir_name,load_counts=False):
83    _, imm_fun, _ = toElfImmFun(f,dir_name,load_counts)
84    return imm_fun
85
86def analyseFunction(f,asm_fs,dir_name,gen_heads,load_counts,emit_graphs, stopAtILP=False):
87    print '========analsying from entry point: %s===========' % f
88    elf_fun,imm_fun, ef = toElfImmFun(f, dir_name,load_counts)
89    #emit graphs with graph-refine
90    if emit_graphs:
91        makeGraph(f,asm_fs)
92
93    imm_fun.process()
94    if gen_heads:
95      print 'generating loop heads'
96      imm_utils.genLoopheads(imm_fun.bin_loops_by_fs,dir_name)
97      print 'loop heads generated'
98      if gen_heads:
99        return None
100      else:
101        import elf_correlate
102        #load the dummy loop counts
103        elf_correlate.immFunc().loaded_loops_by_fs = elf_correlate.loadCounts(dir_name)
104
105    #if emit_graphs:
106        #toDot(imm_fun)
107        #toDotBB(imm_fun)
108
109    emitter = chronos.emitter.ChronosEmitter(dir_name, f, imm_fun)
110    emitter.emitTopLevelFunction()
111
112    imm_file_name = emitter.imm_file_name
113
114    ret_g = getWcetFromImm(imm_file_name, stopAtILP)
115    if not ret_g:
116      return None
117    wcet, out_chronos_gg,err_chronos_gg = ret_g
118    if wcet:
119      print 'G2G-Chronos extracted, wcet: %s\n' % wcet
120      return wcet
121    return None
122
123def init(dir_name):
124    '''setup the target and initialise the elfFile'''
125    target_objects.load_target(dir_name)
126    sys.setrecursionlimit(2000)
127    import graph_refine.stack_logic as stack_logic
128    stack_logic.add_hooks ()
129    #silence graph-refine outputs that  we don't care about when doing wcet
130    def silent_tracer (s,v):
131        if s.startswith('Loop') or re.search(r'\s*\(=',s) or re.search(r'\s*\(',s):
132          return
133        if s.startswith('requests') or s.startswith('Result:') or s.startswith('Now'):
134          return
135        if s.startswith('testing') or s.startswith('done') or s.startswith('rep_graph'):
136          return
137        if s.startswith('Testing') or s.startswith('Group'):
138          return
139        print s
140
141    target_objects.tracer[0] = silent_tracer
142    elf_parser.parseElf(dir_name)
143    asm_fs = elfFile().asm_fs
144    tran_call_graph = call_graph_utils.transitiveCallGraph(asm_fs,dir_name,dummy_funs)
145
146    elfFile().tcg = tran_call_graph
147    elfFile().asm_idents = None
148    elfFile().immed = None
149    return asm_fs
150
151def bench(dir_name, entry_point_function, gen_heads,load_counts, interactive, parse_only=False, conflict_file=None):
152    asm_fs = init(dir_name)
153    functions = target_objects.functions
154    if parse_only or interactive:
155        t = entry_point_function
156        i = toImmFun(t,dir_name,load_counts=load_counts)
157        i.process()
158        return
159    dn = dir_name
160    emit_graphs = False
161    wcet = analyseFunction(entry_point_function,asm_fs, dir_name, gen_heads, load_counts, emit_graphs=emit_graphs)
162    return wcet
163
164