"Fossies" - the Fresh Open Source Software Archive

Member "sysdig-0.26.1/userspace/sysdig/chisels/flame.lua" (24 May 2019, 12298 Bytes) of package /linux/misc/sysdig-0.26.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Lua source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 --[[
    2 Copyright (C) 2013-2018 Draios Inc dba Sysdig.
    3 
    4 This file is part of sysdig.
    5 
    6 Licensed under the Apache License, Version 2.0 (the "License");
    7 you may not use this file except in compliance with the License.
    8 You may obtain a copy of the License at
    9 
   10     http://www.apache.org/licenses/LICENSE-2.0
   11 
   12 Unless required by applicable law or agreed to in writing, software
   13 distributed under the License is distributed on an "AS IS" BASIS,
   14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   15 See the License for the specific language governing permissions and
   16 limitations under the License.
   17 
   18 --]]
   19 
   20 
   21 -- Chisel description
   22 disabled_description = "Flame graph generator";
   23 short_description = "Sysdig trace flame graph builder";
   24 category = "Performance";
   25 
   26 -- Chisel argument list
   27 args =
   28 {
   29 }
   30 
   31 require "common"
   32 json = require ("dkjson")
   33 
   34 local CAPTURE_LOGS = true
   35 
   36 local spans = {}
   37 local fid
   38 local flatency
   39 local fcontname
   40 local fexe
   41 local fbuf
   42 local fdir
   43 local ftime
   44 local MAX_DEPTH = 256
   45 local avg_tree = {}
   46 local full_tree = {}
   47 local max_tree = {}
   48 local min_tree = {}
   49 local logs_tree = {}
   50 local next = next -- make next faster
   51 local PAGE_HEADER = [[<!DOCTYPE html>
   52 <html>
   53     <head>
   54         <meta charset="utf-8">
   55         <meta http-equiv="X-UA-Compatible" content="IE=edge">
   56         <title>Flame UI</title>
   57         <meta name="description" content="">
   58         <meta name="viewport" content="width=device-width, initial-scale=1">
   59 
   60         
   61 <meta name="flame-ui/config/environment" content="%7B%22modulePrefix%22%3A%22flame-ui%22%2C%22environment%22%3A%22development%22%2C%22baseURL%22%3A%22/%22%2C%22locationType%22%3A%22hash%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%7D%2C%22APP%22%3A%7B%22name%22%3A%22flame-ui%22%2C%22version%22%3A%220.0.0+3fc5f790%22%7D%2C%22contentSecurityPolicy%22%3A%7B%22default-src%22%3A%22%27none%27%22%2C%22script-src%22%3A%22%27self%27%20%27unsafe-inline%27%22%2C%22style-src%22%3A%22%27self%27%20%27unsafe-inline%27%22%2C%22font-src%22%3A%22%27self%27%22%2C%22connect-src%22%3A%22%27self%27%22%2C%22img-src%22%3A%22%27self%27%22%2C%22media-src%22%3A%22%27self%27%22%7D%2C%22contentSecurityPolicyHeader%22%3A%22Content-Security-Policy-Report-Only%22%2C%22exportApplicationGlobal%22%3Atrue%7D" />
   62 
   63         <link rel="stylesheet" href="https://cdn.rawgit.com/draios/flame-ui/master/build/assets/vendor.css">
   64         <link rel="stylesheet" href="https://cdn.rawgit.com/draios/flame-ui/master/build/assets/flame-ui.css">
   65 
   66         
   67     </head>
   68     <body>
   69         
   70 
   71         <script src="https://cdn.rawgit.com/draios/flame-ui/master/build/assets/vendor.js"></script>
   72         <script src="https://cdn.rawgit.com/draios/flame-ui/master/build/assets/flame-ui.js"></script>
   73 
   74         
   75 
   76         <script>
   77             window.transactions = {
   78 ]]
   79 
   80 local PAGE_TRAILER = [[            };
   81         </script>
   82     </body>
   83 </html>
   84 
   85 ]]
   86 
   87 -- Argument notification callback
   88 function on_set_arg(name, val)
   89     return true
   90 end
   91 
   92 -- Initialization callback
   93 function on_init()
   94     -- Request the fields needed for this chisel
   95     for j = 0, MAX_DEPTH do
   96         local fname = "span.tag[" .. j .. "]"
   97         local minfo = chisel.request_field(fname)
   98         spans[j] = minfo
   99     end
  100     
  101     fid = chisel.request_field("span.id")
  102     flatency = chisel.request_field("span.duration")
  103     fcontname = chisel.request_field("container.name")
  104     fexe = chisel.request_field("proc.exeline")
  105     fbuf = chisel.request_field("evt.buffer")
  106     fdir = chisel.request_field("evt.dir")
  107     ftid = chisel.request_field("thread.tid")
  108     ftime = chisel.request_field("evt.time")
  109 
  110     -- set the filter
  111     if CAPTURE_LOGS then
  112         chisel.set_filter("(evt.type=tracer) or (evt.is_io_write=true and evt.dir=< and (fd.num=1 or fd.num=2 or fd.name contains log))")
  113     else
  114         chisel.set_filter("evt.type=tracer and evt.dir=<")
  115     end
  116 
  117     return true
  118 end
  119 
  120 -- Add a log entry into the proper place(s) in the log table
  121 function collect_log(tid_tree)
  122     for k,entry in pairs(tid_tree) do
  123         while true do
  124             local lastv = v
  125             k,v = next(entry)
  126             if v == nil then
  127                 if lastv.l == nil then
  128                     lastv.l = {}
  129                 end
  130 
  131                 local etime = evt.field(ftime)
  132                 local buf = evt.field(fbuf)
  133                 local tid = evt.field(ftid)
  134                 local hi, low = evt.get_ts()
  135 
  136                 local linedata = {t=etime, th=hi, tl=low, tid=tid, b=buf}
  137 
  138                 table.insert(lastv.l, linedata)
  139 --print("*** " .. evt.get_num() .. " " .. linedata)
  140 --print(st(logs_tree))
  141 --print("***************************")
  142                 return
  143             end
  144 
  145             entry = v.ch
  146         end
  147     end
  148 end
  149 
  150 -- Parse a tracer enter event and update the logs_tree table
  151 function parse_tracer_enter(logtable_cur, hr)
  152     for j = 1, #hr do
  153         local mv = hr[j]
  154         
  155         if mv == nil then
  156             break
  157         end
  158         
  159         if logtable_cur[mv] == nil then
  160             logtable_cur[mv] = {ch={}}
  161         end
  162 
  163         if j == #hr then
  164             logtable_cur[mv].r=true
  165         end
  166 
  167         logtable_cur = logtable_cur[mv].ch
  168     end
  169 end
  170 
  171 -- Parse a tracer exit event and update the given transaction entry
  172 function parse_tracer_exit(mrk_cur, logtable_cur, hr, latency, contname, exe, id)
  173     local res = false
  174     local parent_has_logs = false;
  175 
  176     for j = 1, #hr do
  177         local mv = hr[j]
  178         if mv == nil or mrk_cur == nil then
  179             break
  180         end
  181         
  182         local has_logtable_entry = (logtable_cur ~= nil and logtable_cur[mv] ~= nil)
  183 
  184 --print("! " .. evt.get_num() .. " " .. j)
  185 --print(parent_has_logs)
  186 --print(logtable_cur[mv].r)
  187         if j == #hr then
  188             local llogs
  189 
  190             if has_logtable_entry and logtable_cur[mv].l ~= nil then
  191                 llogs = logtable_cur[mv].l
  192             else
  193                 llogs = nil
  194             end
  195 
  196 --print("################ " .. evt.get_num() .. " " .. st(logs_tree))
  197             if mrk_cur[mv] == nil then
  198                 mrk_cur[mv] = {t=latency, tt=latency, cont=contname, exe=exe, c=1, logs=llogs}
  199                 if j == 1 then
  200                     mrk_cur[mv].n = 0
  201                 end
  202             else
  203                 mrk_cur[mv]["tt"] = mrk_cur[mv]["tt"] + latency
  204                 mrk_cur[mv]["cont"] = contname
  205                 mrk_cur[mv]["exe"] = exe
  206                 mrk_cur[mv]["c"] = 1
  207                 mrk_cur[mv]["logs"] = llogs
  208             end
  209 
  210 --print("################ " .. evt.get_num())
  211 --print(st(logs_tree))
  212 --print("## " .. evt.get_num())
  213 --print(st(logtable_cur[mv].r))
  214 
  215             if has_logtable_entry and parent_has_logs == false then
  216                 res = true
  217             else
  218                 logtable_cur[mv] = nil
  219                 has_logtable_entry = false
  220                 logtable_cur = nil
  221             end
  222         elseif j == (#hr - 1) then
  223             if mrk_cur[mv] == nil then
  224                 mrk_cur[mv] = {tt=0}
  225                 if j == 1 then
  226                     mrk_cur[mv].n = 0
  227                 end
  228             end
  229         else
  230             if mrk_cur[mv] == nil then
  231                 mrk_cur[mv] = {tt=0}
  232                 if j == 1 then
  233                     mrk_cur[mv].n = 0
  234                     mrk_cur[mv]["id"] = id
  235                 end
  236             end
  237         end
  238                 
  239         if mrk_cur[mv]["ch"] == nil then
  240             mrk_cur[mv]["ch"] = {}
  241         end
  242         
  243         if #hr == 1 then
  244             mrk_cur[mv].n = mrk_cur[mv].n + 1
  245         end
  246 
  247         -- end of node parsing, update pointers to movo to the child
  248         if has_logtable_entry then
  249             parent_has_logs = (logtable_cur[mv].r ~= nil)
  250         end
  251 
  252         mrk_cur = mrk_cur[mv].ch
  253 
  254         if logtable_cur ~= nil then
  255             logtable_cur = logtable_cur[mv].ch
  256         end
  257     end
  258 
  259     return res
  260 end
  261 
  262 -- Event parsing callback
  263 function on_event()
  264     local etype = evt.get_type()
  265 
  266     if etype ~= "tracer" then
  267         local tid = evt.field(ftid)
  268 
  269         if logs_tree[tid] == nil then
  270             return
  271         else
  272             collect_log(logs_tree[tid])
  273         end
  274 
  275         return
  276     end
  277 
  278     local latency = evt.field(flatency)
  279     local contname = evt.field(fcontname)
  280     local id = evt.field(fid)
  281     local exe = evt.field(fexe)
  282     local hr = {}
  283     local full_trs = nil
  284     local dir = evt.field(fdir)
  285     local tid = evt.field(ftid)
  286 
  287     for j = 0, MAX_DEPTH do
  288         hr[j + 1] = evt.field(spans[j])
  289     end
  290 
  291     if dir == ">" then
  292         if logs_tree[tid] == nil then
  293             logs_tree[tid] = {}
  294         end
  295 
  296         local idt = logs_tree[tid][id]
  297 
  298         if idt == nil then
  299             logs_tree[tid][id] = {}
  300             idt = logs_tree[tid][id]            
  301         end
  302 
  303         parse_tracer_enter(idt, hr)
  304         return true
  305     else
  306         if latency == nil then
  307             return true
  308         end
  309 
  310         if full_tree[id] == nil then
  311             full_tree[id] = {}
  312         end
  313 
  314         -- find the logs for this transaction span
  315         local logs
  316 
  317         if logs_tree[tid] == nil then
  318             logs = nil
  319         else
  320             if logs_tree[tid][id] == nil then
  321                 logs = nil
  322             else
  323                 logs = logs_tree[tid][id]
  324             end
  325         end
  326 
  327     if parse_tracer_exit(full_tree[id], logs, hr, latency, contname, exe, id) then
  328 --print(st(logs_tree))
  329 --print("------------ " .. evt.get_num())
  330 --print(st(full_tree))
  331 --print("---------------------------------------------------")
  332 
  333             logs_tree[tid][id] = nil
  334 
  335             if next(logs_tree[tid]) == nil then
  336                 logs_tree[tid] = nil
  337             end
  338 
  339         end
  340 
  341         return true
  342     end
  343 end
  344 
  345 function calculate_t_in_node(node)
  346     local totchtime = 0
  347     local maxchtime = 0
  348     local nconc = 0
  349     local ch_to_keep
  350 
  351     if node.ch then
  352         for k,d in pairs(node.ch) do
  353             local nv = calculate_t_in_node(d)
  354 
  355             totchtime = totchtime + nv
  356 
  357             if nv > maxchtime then
  358                 maxchtime = nv
  359                 ch_to_keep = d
  360             end
  361 
  362             nconc = nconc + 1
  363         end
  364     end
  365 
  366     if node.tt >= totchtime then
  367         node.t = node.tt - totchtime
  368     else
  369         node.t = node.tt - maxchtime
  370         node.nconc = nconc
  371 
  372         for k,d in pairs(node.ch) do
  373             if d ~= ch_to_keep then
  374                 node.ch[k] = nil
  375             end
  376         end
  377 
  378     end
  379 
  380     return node.tt
  381 end
  382 
  383 function normalize(node, factor)
  384     node.t = node.t / factor
  385     node.tt = node.tt / factor
  386     if node.ch then
  387         for k,d in pairs(node.ch) do
  388             normalize(d, factor)
  389         end
  390     end
  391 end
  392 
  393 function is_transaction_complete(node)
  394     if node.c ~= 1 then
  395         return false
  396     end
  397 
  398     if node.ch then
  399         for k,d in pairs(node.ch) do
  400             if is_transaction_complete(d) == false then
  401                 return false
  402             end
  403         end
  404     end
  405 
  406     return true
  407 end
  408 
  409 function update_avg_tree(dsttree, key, val)
  410     if dsttree[key] == nil then
  411         dsttree[key] = copytable(val)
  412         return
  413     else
  414         dsttree[key].tt = dsttree[key].tt + val.tt
  415 
  416         if dsttree[key].n then
  417             dsttree[key].n = dsttree[key].n + 1
  418         end
  419 
  420         if val.logs then
  421             if dsttree[key].logs == nil then
  422                 dsttree[key].logs = {}
  423             end
  424 
  425             concattable(dsttree[key].logs, val.logs)
  426         end
  427     end
  428 
  429     if val.ch then
  430         if dsttree[key].ch == nil then
  431             dsttree[key].ch = {}
  432         end
  433 
  434         for k,d in pairs(val.ch) do
  435             update_avg_tree(dsttree[key].ch, k, d)
  436         end
  437     end
  438 end
  439 
  440 function update_max_tree(dsttree, key, val)
  441     if dsttree[key] == nil then
  442         dsttree[key] = val
  443         return
  444     else
  445         if val.tt > dsttree[key].tt then
  446             dsttree[key] = val
  447         end
  448     end
  449 end
  450 
  451 function update_min_tree(dsttree, key, val)
  452     if dsttree[key] == nil then
  453         dsttree[key] = val
  454         return
  455     else
  456         if val.tt < dsttree[key].tt then
  457             dsttree[key] = val
  458         end
  459     end
  460 end
  461 
  462 -- This processes the transaction list to extract and aggregate the transactions to emit
  463 function collapse_tree()
  464     -- scan the transaction list
  465     for i,v in pairs(full_tree) do
  466         local ttt = 0
  467         for key,val in pairs(v) do
  468             ttt = ttt + val.tt
  469             if is_transaction_complete(val) then
  470                 update_avg_tree(avg_tree, key, val)
  471                 update_max_tree(max_tree, key, val)
  472                 update_min_tree(min_tree, key, val)
  473             end
  474         end
  475     end
  476 end
  477 
  478 -- Called by the engine at the end of the capture (Ctrl-C)
  479 function on_capture_end()
  480 --print(st(full_tree))
  481     -- Process the list and create the required transactions
  482     collapse_tree()
  483 
  484     -- calculate the unique time spent in each node
  485     for i,v in pairs(avg_tree) do
  486         calculate_t_in_node(v)
  487     end
  488 
  489     -- normalize each root span tree
  490     for i,v in pairs(avg_tree) do
  491         normalize(v, v.n)
  492     end
  493 
  494     print(PAGE_HEADER)
  495 
  496     -- emit the average transaction
  497     local AvgData = {}
  498     AvgData[""] = {ch=avg_tree, t=0, tt=0}
  499     local str = json.encode(AvgData, { indent = true })
  500     print('"avg": ' .. str .. ",")
  501 
  502     -- normalize the best transaction
  503     for i,v in pairs(min_tree) do
  504         calculate_t_in_node(v)
  505     end
  506 
  507     -- emit the best transaction
  508     local tdata = {}
  509     tdata[""] = {ch=min_tree, t=0, tt=0}
  510     local str = json.encode(tdata, { indent = true })
  511     print('"min": ' .. str .. ",")
  512 
  513     -- normalize the worst transaction
  514     for i,v in pairs(max_tree) do
  515         calculate_t_in_node(v)
  516     end
  517 
  518     -- emit the worst transaction
  519     local tdata = {}
  520     tdata[""] = {ch=max_tree, t=0, tt=0}
  521     local str = json.encode(tdata, { indent = true })
  522     print('"max": ' .. str .. ",")
  523 
  524     print(PAGE_TRAILER)
  525 end