aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/LuaSL/testLua/yueliang-0.4.1/orig-5.0.3/tools/call_graph.lua
blob: 5c60e17b0f127583e4282afb1828d18815c1d801 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
--[[--------------------------------------------------------------------

  call_graph.lua
  Call graph generator.
  This file is part of Yueliang.

  Copyright (c) 2005-2006 Kein-Hong Man <khman@users.sf.net>
  The COPYRIGHT file describes the conditions
  under which this software may be distributed.

  See the ChangeLog for more information.

----------------------------------------------------------------------]]

--[[--------------------------------------------------------------------
-- Notes:
-- * the call tracer wraps function calls in tables to do its work
-- * not very elegant as the namespace of the table/module is affected
-- * tracing using the debugger is probably much more powerful...
-- * use of braces {} allows editors to match braces in the output
--   and do folding, if such facilities are available; for example, the
--   output looks better if Lua syntax highlighting is used on SciTE
----------------------------------------------------------------------]]

------------------------------------------------------------------------
-- options
------------------------------------------------------------------------

local SHOW_EXPDESC = true       -- show expdesc struct data

------------------------------------------------------------------------
-- load and initialize modules
------------------------------------------------------------------------
require("../lzio.lua")
require("../llex.lua")
require("../lopcodes.lua")
require("../ldump.lua")
require("../lcode.lua")
require("../lparser.lua")

function lua_assert(test)
  if not test then error("assertion failed!") end
end
luaX:init()
local LuaState = {}

------------------------------------------------------------------------
-- call graph generator
-- * (1) logging functions, (2) the wrapper initializer itself
------------------------------------------------------------------------

llog = {}

------------------------------------------------------------------------
-- initialize log file; the usual mode is append; can use stdout/stderr
------------------------------------------------------------------------
function llog:init(filename)
  if filename == "stdout" then self.h = io.stdout
  elseif filename == "stderr" then self.h = io.stderr
  else
    self.h = io.open(filename, "ab")
    if not self.h then
      error("can't open log file "..filename.."for writing")
    end
  end
  self.h:write("\n-- start of log --\n\n")
end

------------------------------------------------------------------------
-- cleanly closes log file
------------------------------------------------------------------------
function llog:exit()
  self.h:write("\n-- end of log --\n\n")
  if self.h ~= io.stdout and self.h ~= io.stderr then
    self.h:close()
  end
end

------------------------------------------------------------------------
-- logs a message at a particular call depth
------------------------------------------------------------------------
function llog:msg(msg, level)
  if level then msg = string.rep("  ", level)..msg end
  self.h:write(msg)
  self.h:flush()
end

------------------------------------------------------------------------
-- set up wrapper functions to do tracing on a per-module basis
------------------------------------------------------------------------
function llog:calltrace(parms)
  ------------------------------------------------------------------
  -- process parameters
  ------------------------------------------------------------------
  local module = parms.module
  local modulename = parms.modulename
  if type(module) ~= "table" then
    error("module table parameter required")
  elseif not modulename then
    error("module name parameter required")
  end
  ------------------------------------------------------------------
  -- use either allow or deny list
  ------------------------------------------------------------------
  local allow = parms.allow or {}
  local deny = parms.deny or {}
  if table.getn(allow) > 0 and table.getn(deny) > 0 then
    error("can't apply both allow and deny lists at the same time")
  end
  ------------------------------------------------------------------
  -- select functions to wrap
  ------------------------------------------------------------------
  local flist = {}
  for i, f in pairs(module) do
    local wrapthis
    if table.getn(allow) > 0 then  -- allow some only
      wrapthis = false
      for j, v in ipairs(allow) do
        if i == v then wrapthis = true; break end
      end
    elseif table.getn(deny) > 0 then  -- deny some only
      wrapthis = true
      for j, v in ipairs(deny) do
        if i == v then wrapthis = false; break end
      end
    else  -- default include
      wrapthis = true
    end
    if wrapthis then flist[i] = f end
  end
  ------------------------------------------------------------------
  -- wrapped function(s) in a module for tracing
  ------------------------------------------------------------------
  llog.level = 0  -- nesting level
  for i, f in pairs(flist) do
    local ModuleName = modulename..":"
    local OldName, OldFunc = i, f
    if type(OldFunc) == "function" then
      local NewName = "__"..OldName
      while module[NewName] ~= nil do  -- avoid collisions
        NewName = "_"..NewName
      end
      module[NewName] = OldFunc
      module[OldName] =
        ----------------------------------------------------------
        -- wrapper function for a module's function
        -- old function XYZ is renamed __XYZ
        ----------------------------------------------------------
        function(self, ...)
          local parms = " ("
          local exps = {}
          -- look for expdesc structs, identify FuncState structs too
          local function checkexpdesc(v)
            local typ = type(v)
            if typ == "table" then
              if v.code then return "func"
              elseif v.L then return "ls"
              elseif v.seminfo then return "token"
              elseif v.k then
                table.insert(exps, v)
                return "exp"..table.getn(exps)
              end
            end
            return typ
          end
          -- format parameters for printing
          for i,v in ipairs(arg) do
            if type(v) == "number" then parms = parms..v..","
            elseif type(v) == "string" then parms = parms.."'"..v.."',"
            elseif type(v) == "boolean" then parms = parms..tostring(v)..","
            elseif SHOW_EXPDESC then parms = parms..checkexpdesc(v)..","
            else parms = parms..type(v)..","
            end
          end
          if table.getn(arg) > 0 then  -- chop last comma
            parms = string.sub(parms, 1, -2)
          end
          -- up level
          llog:msg(ModuleName..OldName..parms..") {\n", llog.level)
          llog.level = llog.level + 1
          -- display contents of expdesc
          if SHOW_EXPDESC and table.getn(exps) > 0 then
            for i,v in ipairs(exps) do
              parms = "k:'"..v.k.."',"
              if v.info then parms = parms.."info:"..v.info.."," end
              if v.aux then parms = parms.."aux:"..v.aux.."," end
              if v.t then parms = parms.."t:"..v.t.."," end
              if v.f then parms = parms.."f:"..v.f.."," end
              parms = string.sub(parms, 1, -2)
              llog:msg("exp"..i.."("..parms..")\n", llog.level)
            end
          end
          -- original function called here...
          local retval = {self[NewName](self, unpack(arg))}
          -- format return values
          local rets = " = "
          for i,v in ipairs(retval) do
            if type(v) == "number" then rets = rets..v..","
            elseif type(v) == "string" then rets = rets.."'"..v.."',"
            elseif type(v) == "boolean" then rets = rets..tostring(v)..","
            else rets = rets..type(v)..","
            end
          end
          if table.getn(retval) > 0 then  -- chop last comma
            rets = string.sub(rets, 1, -2)
          else
            rets = ""
          end
          -- down level
          llog.level = llog.level - 1
          llog:msg("} "..ModuleName..OldName..rets.."\n", llog.level)
          return unpack(retval)
        end--function
        ----------------------------------------------------------
      --print("patched "..OldName)
    end--if
  end--for
end

------------------------------------------------------------------------
-- testing here
-- * allow/deny works a bit like a somewhat similar Apache syntax
-- * e.g. to show only function 'lex' and 'save' -> allow={"lex","save",}
--        to not show function 'save_and_next' -> deny={"save_and_next",}
-- * you can't do both allow and deny at the same time
------------------------------------------------------------------------

-- select the file or stream to output to
--llog:init("calls.log")
llog:init("stdout")

-- select modules to trace
llog:calltrace{module=luaX, modulename="luaX", allow={"lex"} }
  -- here we trace only the main lex() function, to avoid showing
  -- too many lexer calls; we want to focus on luaY and luaK
llog:calltrace{module=luaY, modulename="luaY", deny={"growvector"} }
  -- growvector() is just a limit checker in Yueliang, so drop it
  -- to simplify the output log
llog:calltrace{module=luaK, modulename="luaK"}
--llog:calltrace{module=luaU, modulename="luaU"}

-- select input stream
local zio = luaZ:init(luaZ:make_getS("local a = 1"), nil, "=string")
--local zio = luaZ:init(luaZ:make_getF("sample.lua"), nil, "@sample.lua")

-- compile the source
local Func = luaY:parser(LuaState, zio, nil)

-- write binary chunk
local Writer, Buff = luaU:make_setF("call_graph.out")
luaU:dump(LuaState, Func, Writer, Buff)

llog:exit()
--end