<commit_before>-- Use of this source code is governed by the Apache 2.0 license; see COPYING.

module(..., package.seeall)

local ffi = require("ffi")
local C = ffi.C
local lib = require("core.lib")
local shm = require("core.shm")
local counter = require("core.counter")
local S = require("syscall")
local histogram = require("core.histogram")
local usage = require("program.top.README_inc")

local long_opts = {
   help = "h", list = "l"
}

function clearterm () io.write('\027[2J') end

function run (args)
   local opt = {}
   local object = nil
   function opt.h (arg) print(usage) main.exit(1) end
   function opt.l (arg) object = arg              end
   args = lib.dogetopt(args, opt, "hl:", long_opts)

   if #args > 1 then print(usage) main.exit(1) end
   local target_pid = select_snabb_instance(args[1])

   if     object then list_shm(target_pid, object)
   else               top(target_pid) end
end

function select_snabb_instance (pid)
   local function compute_snabb_instances()
      -- Produces set of snabb instances, excluding this one.
      local pids = {}
      local my_pid = S.getpid()
      for _, name in ipairs(shm.children("/")) do
         -- This could fail as the name could be for example "by-name"
         local p = tonumber(name)
         if p and p ~= my_pid then table.insert(pids, name) end
      end
      return pids
   end

   local instances = compute_snabb_instances()

   if pid then
      pid = tostring(pid)
      -- Try to use given pid
      for _, instance in ipairs(instances) do
         if instance == pid then return pid end
      end
      print("No such Snabb instance: "..pid)
   elseif #instances == 1 then return instances[1]
   elseif #instances <= 0 then print("No Snabb instance found.")
   else
      print("Multiple Snabb instances found. Select one:")
      for _, instance in ipairs(instances) do print(instance) end
   end
   main.exit(1)
end

function list_shm (pid, object)
   local frame = shm.open_frame("/"..pid.."/"..object)
   local sorted = {}
   for name, _ in pairs(frame) do table.insert(sorted, name) end
   table.sort(sorted)
   for _, name in ipairs(sorted) do
      if name ~= 'path' and name ~= 'specs' and  name ~= 'readonly' then
         print_row({30, 30}, {name, tostring(frame[name])})
      end
   end
   shm.delete_frame(frame)
end

function top (instance_pid)
   local instance_tree = "/"..instance_pid
   local counters = open_counters(instance_tree)
   local configs = 0
   local last_stats = nil
   while (true) do
      if configs < counter.read(counters.engine.configs) then
         -- If a (new) config is loaded we (re)open the link counters.
         open_link_counters(counters, instance_tree)
      end
      local new_stats = get_stats(counters)
      if last_stats then
         clearterm()
         print_global_metrics(new_stats, last_stats)
         io.write("\n")
         print_latency_metrics(new_stats, last_stats)
         print_link_metrics(new_stats, last_stats)
         io.flush()
      end
      last_stats = new_stats
      C.sleep(1)
   end
end

function open_counters (tree)
   local counters = {}
   counters.engine = shm.open_frame(tree.."/engine")
   counters.links = {} -- These will be populated on demand.<commit_msg>top: fix bug where counters were redundantly reopened<commit_after>-- Use of this source code is governed by the Apache 2.0 license; see COPYING.

module(..., package.seeall)

local ffi = require("ffi")
local C = ffi.C
local lib = require("core.lib")
local shm = require("core.shm")
local counter = require("core.counter")
local S = require("syscall")
local histogram = require("core.histogram")
local usage = require("program.top.README_inc")

local long_opts = {
   help = "h", list = "l"
}

function clearterm () io.write('\027[2J') end

function run (args)
   local opt = {}
   local object = nil
   function opt.h (arg) print(usage) main.exit(1) end
   function opt.l (arg) object = arg              end
   args = lib.dogetopt(args, opt, "hl:", long_opts)

   if #args > 1 then print(usage) main.exit(1) end
   local target_pid = select_snabb_instance(args[1])

   if     object then list_shm(target_pid, object)
   else               top(target_pid) end
end

function select_snabb_instance (pid)
   local function compute_snabb_instances()
      -- Produces set of snabb instances, excluding this one.
      local pids = {}
      local my_pid = S.getpid()
      for _, name in ipairs(shm.children("/")) do
         -- This could fail as the name could be for example "by-name"
         local p = tonumber(name)
         if p and p ~= my_pid then table.insert(pids, name) end
      end
      return pids
   end

   local instances = compute_snabb_instances()

   if pid then
      pid = tostring(pid)
      -- Try to use given pid
      for _, instance in ipairs(instances) do
         if instance == pid then return pid end
      end
      print("No such Snabb instance: "..pid)
   elseif #instances == 1 then return instances[1]
   elseif #instances <= 0 then print("No Snabb instance found.")
   else
      print("Multiple Snabb instances found. Select one:")
      for _, instance in ipairs(instances) do print(instance) end
   end
   main.exit(1)
end

function list_shm (pid, object)
   local frame = shm.open_frame("/"..pid.."/"..object)
   local sorted = {}
   for name, _ in pairs(frame) do table.insert(sorted, name) end
   table.sort(sorted)
   for _, name in ipairs(sorted) do
      if name ~= 'path' and name ~= 'specs' and  name ~= 'readonly' then
         print_row({30, 30}, {name, tostring(frame[name])})
      end
   end
   shm.delete_frame(frame)
end

function top (instance_pid)
   local instance_tree = "/"..instance_pid
   local counters = open_counters(instance_tree)
   local configs = 0
   local last_stats = nil
   while (true) do
      local current = counter.read(counters.engine.configs)
      if configs < current then
         configs = current
         -- If a (new) config is loaded we (re)open the link counters.
         open_link_counters(counters, instance_tree)
      end
      local new_stats = get_stats(counters)
      if last_stats then
         clearterm()
         print_global_metrics(new_stats, last_stats)
         io.write("\n")
         print_latency_metrics(new_stats, last_stats)
         print_link_metrics(new_stats, last_stats)
         io.flush()
      end
      last_stats = new_stats
      C.sleep(1)
   end
end

function open_counters (tree)
   local counters = {}
   counters.engine = shm.open_frame(tree.."/engine")
   counters.links = {} -- These will be populated on demand.
{'fraction_non_alphanumeric': 0.10679021722618867, 'fraction_numerical': 0.006076257025672186, 'mean_word_length': 2.8057803468208093, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6683663', 'n_tokens_mistral': 2113, 'n_tokens_neox': 2065, 'n_words': 651}