Edit File: common.lua
--[[ Copyright (C) 2013-2018 Draios Inc dba Sysdig. This file is part of sysdig. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --]] --[[ This file contains a bunch of functions that are helpful in multiple scripts ]]-- --[[ Serialize the content of a table into a string ]]-- function st(val, name, skipnewlines, depth) skipnewlines = skipnewlines or false depth = depth or 0 local tmp = string.rep(" ", depth) if name then tmp = tmp .. name .. " = " end if type(val) == "table" then tmp = tmp .. "{" .. (not skipnewlines and "\n" or "") for k, v in pairs(val) do tmp = tmp .. st(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and "\n" or "") end tmp = tmp .. string.rep(" ", depth) .. "}" elseif type(val) == "number" then tmp = tmp .. tostring(val) elseif type(val) == "string" then tmp = tmp .. string.format("%q", val) elseif type(val) == "boolean" then tmp = tmp .. (val and "true" or "false") else tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\"" end return tmp end --[[ Extends a string to newlen with spaces ]]-- function extend_string(s, newlen) if #s < newlen then local ccs = " " s = s .. string.sub(ccs, 0, newlen - #s) return s else if newlen > 0 then return (string.sub(s, 0, newlen - 1) .. " ") else return "" end end end --[[ Basic string split. ]]-- function split(s, delimiter) local result = {} for match in (s..delimiter):gmatch("(.-)"..delimiter) do table.insert(result, match) end return result end --[[ Substring matching. ]]-- function starts_with(str, prefix) return prefix == "" or str:sub(1, #prefix) == prefix end function ends_with(str, suffix) return suffix == "" or str:sub(-#suffix) == suffix end --[[ convert a number into a byte representation. E.g. 1230 becomes 1.23K ]]-- function format_bytes(val) if val > (1024 * 1024 * 1024 * 1024 * 1024) then return string.format("%.2fP", val / (1024 * 1024 * 1024 * 1024 * 1024)) elseif val > (1024 * 1024 * 1024 * 1024) then return string.format("%.2fT", val / (1024 * 1024 * 1024 * 1024)) elseif val > (1024 * 1024 * 1024) then return string.format("%.2fG", val / (1024 * 1024 * 1024)) elseif val > (1024 * 1024) then return string.format("%.2fM", val / (1024 * 1024)) elseif val > 1024 then return string.format("%.2fKB", val / (1024)) else return string.format("%dB", val) end end --[[ convert a nanosecond time interval into a s.ns representation. E.g. 1100000000 becomes 1.1s ]]-- ONE_S_IN_NS=1000000000 ONE_MS_IN_NS=1000000 ONE_US_IN_NS=1000 function format_time_interval(val) if val >= (ONE_S_IN_NS) then return string.format("%u.%02us", math.floor(val / ONE_S_IN_NS), (val % ONE_S_IN_NS) / 10000000) elseif val >= (ONE_S_IN_NS / 100) then return string.format("%ums", math.floor(val / (ONE_S_IN_NS / 1000))) elseif val >= (ONE_S_IN_NS / 1000) then return string.format("%u.%02ums", math.floor(val / (ONE_S_IN_NS / 1000)), (val % ONE_MS_IN_NS) / 10000) elseif val >= (ONE_S_IN_NS / 100000) then return string.format("%uus", math.floor(val / (ONE_S_IN_NS / 1000000))) elseif val >= (ONE_S_IN_NS / 1000000) then return string.format("%u.%02uus", math.floor(val / (ONE_S_IN_NS / 1000000)), (val % ONE_US_IN_NS) / 10) else return string.format("%uns", val) end end --[[ extract the top num entries from the table t, after sorting them based on the entry value using the function order() ]]-- function pairs_top_by_val(t, num, order) local keys = {} for k in pairs(t) do keys[#keys+1] = k end table.sort(keys, function(a,b) return order(t, a, b) end) local i = 0 return function() i = i + 1 if (num == 0 or i <= num) and keys[i] then return keys[i], t[keys[i]] end end end --[[ Timestamp <-> string conversion ]]-- function ts_to_str(tshi, tslo) return string.format("%u%.9u", tshi, tslo) end --[[ Pick a key-value table and render it to the console in sorted top format ]]-- json = require ("dkjson") function print_sorted_table(stable, ts_s, ts_ns, timedelta, viz_info) local sorted_grtable = pairs_top_by_val(stable, viz_info.top_number, function(t,a,b) return t[b] < t[a] end) if viz_info.output_format == "json" then local jdata = {} local j = 1 for k,v in sorted_grtable do local vals = split(k, "\001\001") vals[#vals + 1] = v jdata[j] = vals j = j + 1 end local jinfo = {} for i, keyname in ipairs(viz_info.key_fld) do jinfo[i] = {name = keyname, desc = viz_info.key_desc[i], is_key = true} end jinfo[3] = {name = viz_info.value_fld, desc = viz_info.value_desc, is_key = false} local res = {ts = sysdig.make_ts(ts_s, ts_ns), data = jdata, info = jinfo} local str = json.encode(res, { indent = true }) print(str) else -- Same size to extend each string local EXTEND_STRING_SIZE = 20 local header = extend_string(viz_info.value_desc, EXTEND_STRING_SIZE) for i, fldname in ipairs(viz_info.key_desc) do header = header .. extend_string(fldname, EXTEND_STRING_SIZE) end print(header) print("--------------------------------------------------------------------------------") for k,v in sorted_grtable do local keystr = "" local singlekeys = split(k, "\001\001") for i, singlekey in ipairs(singlekeys) do if i < #singlekeys then keystr = keystr .. extend_string(string.sub(singlekey, 0, EXTEND_STRING_SIZE), EXTEND_STRING_SIZE) else keystr = keystr .. singlekey end end if viz_info.value_units == "none" then print(extend_string(tostring(v), EXTEND_STRING_SIZE) .. keystr) elseif viz_info.value_units == "bytes" then print(extend_string(format_bytes(v), EXTEND_STRING_SIZE) .. keystr) elseif viz_info.value_units == "time" then print(extend_string(format_time_interval(v), EXTEND_STRING_SIZE) .. keystr) elseif viz_info.value_units == "timepct" then if timedelta > 0 then pctstr = string.format("%.2f%%", v / timedelta * 100) else pctstr = "0.00%" end print(extend_string(pctstr, EXTEND_STRING_SIZE) .. keystr) end end end end --[[ Try to convert user input to a number using tonumber(). If tonumber() returns 'nil', print an error message to the user and exit, otherwise return tonumber(value). ]]-- function parse_numeric_input(value, name) val = tonumber(value) if val == nil then print(string.format("Input %s must be a number.", name)) require ("os") os.exit() end return val end --[[ Perform a deep copy of a table. ]]-- function copytable(orig) local orig_type = type(orig) local copy if orig_type == 'table' then copy = {} for orig_key, orig_value in next, orig, nil do copy[copytable(orig_key)] = copytable(orig_value) end setmetatable(copy, copytable(getmetatable(orig))) else -- number, string, boolean, etc copy = orig end return copy end --[[ Add the content of a table at the end of another one. ]]-- function concattable(dst, src) for i=1,#src do dst[#dst + 1] = src[i] end return dst end --[[ return the type of a variable. ]]-- function typeof(var) local _type = type(var); if(_type ~= "table" and _type ~= "userdata") then return _type; end local _meta = getmetatable(var); if(_meta ~= nil and _meta._NAME ~= nil) then return _meta._NAME; else return _type; end end