Browse Source

[#85496] Use sockets for communication

main
Przemysław Barcicki 3 months ago
parent
commit
669c9bfebd
  1. 5
      README.md
  2. 6
      common.py
  3. 159
      sargraph.py
  4. 407
      watch.py

5
README.md

@ -1,6 +1,6 @@
# sargraph
Copyright (c) 2019-2023 [Antmicro](https://www.antmicro.com)
Copyright (c) 2019-2026 [Antmicro](https://www.antmicro.com)
This is a simple python tool that uses "sysstat" ("sar") to save information on CPU, RAM and disk usage.
The process runs in background and can be controlled with a set of sargraph sub-commands.
@ -10,7 +10,7 @@ Supported plot formats are PNG, SVG and ASCII, they are determined by filename e
# Install requirements
The sargraph requires `gnuplot`, `sysstat` (`sar`), `python3`, `coreutils` and `screen` to operate.
The sargraph requires `gnuplot`, `sysstat` (`sar`), `python3` and `coreutils` to operate.
In Debian you can install them with:
```
@ -22,7 +22,6 @@ apt-get install -qqy --no-install-recommends \
gnuplot-nox \
python3 \
python3-pip \
screen \
sysstat
# install Python dependencies

6
common.py

@ -39,7 +39,11 @@ def run_or_fail(*argv, **kwargs):
# Check if a process is running
def pid_running(pid):
return os.path.exists(f"/proc/{pid}")
return file_exists(f"/proc/{pid}")
# Check whether path exists
def file_exists(filename: str):
return os.path.exists(filename)
# Convert a string to float, also when the separator is a comma

159
sargraph.py

@ -11,12 +11,13 @@ import time
import graph
import watch
import warnings
from common import *
# Declare and parse command line flags
parser = argparse.ArgumentParser()
parser.add_argument('session', metavar='SESSION-NAME', type=str, nargs='?', default=None, help='sargraph session name')
parser.add_argument('session', metavar='SESSION-NAME', type=str, nargs='?', help='sargraph session name')
parser.add_argument('command', metavar='COMMAND', type=str, nargs='*', help='send command')
parser.add_argument('-f', metavar='DEVICE-NAME', type=str, nargs='?', default=None, dest='fsdev', help='observe a chosen filesystem')
parser.add_argument('-m', metavar='MOUNT-DIR', type=str, nargs='?', default=None, dest='fspath', help='observe a chosen filesystem')
@ -29,23 +30,16 @@ parser.add_argument('-C', metavar='UDP_COOKIE', type=str, nargs='?', defa
parser.add_argument('-p', action='store_true', dest='psutil', help='use psutil instead of sar')
args = parser.parse_args()
def send(sid, msg):
p = subprocess.Popen(["screen", "-S", sid, "-X", "stuff", f"{msg}\n"])
while p.poll() is None:
time.sleep(0.1)
# Check if sar is available
if not is_darwin():
p = run_or_fail("sar", "-V", stdout=subprocess.PIPE)
def send(session: str, message: str):
sock, socket_path = watch.get_socket(session)
if not file_exists(socket_path):
fail(f"Session '{session}' does not exist")
# Check if screen is available
p = run_or_fail("screen", "-v", stdout=subprocess.PIPE)
version = scan("Screen version (\\d+)", int, p.stdout.readline().decode())
if version is None:
fail("'screen' tool returned unknown output")
sock.connect(socket_path)
sock.send(message.encode("utf-8"))
sock.close()
# If the script was run with no parameters, run in background and gather data
if args.session is None:
def create_session():
# Find requested disk device
if args.fspath:
args.fspath = os.path.realpath(args.fspath)
@ -53,69 +47,98 @@ if args.session is None:
while args.fsdev is None:
args.fsdev = scan(f"^(/dev/\\S+)\\s+{re.escape(args.fspath)}\\s+", str, f.readline())
if not args.fsdev:
fail(f"no device is mounted on {args.fspath}")
fail(f"No device is mounted on {args.fspath}")
params = (args.session, args.fsdev, args.iface, args.tmpfs, args.cache, args.udp, args.udp_cookie)
if is_darwin() or args.psutil:
watcher = watch.PsUtilWatcher(*params)
else:
watcher = watch.SarWatcher(*params)
watch.watch(args.name, args.fsdev, args.iface, args.tmpfs, args.cache, args.psutil, args.udp, args.udp_cookie)
watcher.start()
sys.exit(0)
# Now handle the commands
# Check if sar is available
if not is_darwin():
p = run_or_fail("sar", "-V", stdout=subprocess.PIPE)
if args.name != "data":
warnings.warn("'-o' is deprecated, session name is default output base name")
# Check if a command was provided
if len(args.command) <= 0:
fail("command not provided")
# Check if a command was provided, if that session exists, yell at user for lack of commands, else spawn
if len(args.command) == 0:
sock, socket_path = watch.get_socket(args.session)
if file_exists(socket_path):
fail("Command not provided")
# Get session name and command name
sid = args.session
cmd = args.command
else:
print(f"Starting sargraph session '{args.session}'")
create_session()
if args.command[0] == "start":
sock, socket_path = watch.get_socket(args.session)
if file_exists(socket_path):
fail("Session with this name already exists")
# Start watcher process
p = subprocess.Popen(
args=[sys.executable, os.path.realpath(__file__), args.session, *sys.argv[3:]],
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
start_new_session=True
)
# Spinloop to see whether the subprocess even starts
attempts = 5
while attempts:
time.sleep(0.1)
if file_exists(socket_path):
print(f"Session '{args.session}' started")
sys.exit(0)
attempts -= 1
if cmd[0] == "start":
print(f"Starting sargraph session '{sid}'")
fail("Session did not start")
# Spawn watcher process, *sys.argv[3:] is all arguments after 'chart start' + '-o [log name]' if not given
if "-o" not in sys.argv:
sys.argv += ["-o", sid]
p = subprocess.Popen(["screen", "-Logfile", f"{sid}.log", "-dmSL", sid, os.path.realpath(__file__), *sys.argv[3:]])
elif args.command[0] == "stop":
_, socket_path = watch.get_socket(args.session)
while p.poll() is None:
time.sleep(0.1)
gpid = 0
j = 0
time.sleep(1)
print(f"Session '{sid}' started")
elif cmd[0] == "stop":
print(f"Terminating sargraph session '{sid}'")
try:
gpid = int(os.popen(f"screen -ls | grep '.{sid}' | tr -d ' \t' | cut -f 1 -d '.'").read())
except:
print("Warning: cannot find pid.")
gpid = -1
if len(cmd) < 2:
send(sid, "command:q:")
print(f"Terminating sargraph session '{args.session}'")
if len(args.command) < 2:
send(args.session, "command:q:")
else:
send(sid, f"command:q:{cmd[1]}")
if gpid == -1:
print("Waiting 3 seconds.")
time.sleep(3)
else:
while pid_running(gpid):
time.sleep(0.25)
elif cmd[0] == "label":
send(args.session, f"command:q:{args.command[1]}")
# Spinloop to see whether the subprocess even dies
attempts = 5
while attempts:
time.sleep(0.5)
if not file_exists(socket_path):
print(f"Session '{args.session}' killed")
sys.exit(0)
attempts -= 1
fail("Session did not respond")
elif args.command[0] == "label":
# Check if the label name was provided
if len(cmd) < 2:
if len(args.command) < 2:
fail("label command requires an additional parameter")
print(f"Adding label '{cmd[1]}' to sargraph session '{sid}'.")
send(sid, f"label:{cmd[1]}")
elif cmd[0] == 'save':
print(f"Saving graph from session '{sid}'.")
if len(cmd) < 2:
send(sid, "command:s:")
print(f"Adding label '{args.command[1]}' to sargraph session '{args.session}'.")
send(args.session, f"label:{args.command[1]}")
elif args.command[0] == 'save':
print(f"Saving graph from session '{args.session}'.")
if len(args.command) < 2:
send(args.session, "command:s:")
else:
send(sid, f"command:s:{cmd[1]}")
elif cmd[0] == 'plot':
if len(cmd) < 2:
graph.graph(sid, args.tmpfs, args.cache)
send(args.session, f"command:s:{args.command[1]}")
elif args.command[0] == 'plot':
if len(args.command) < 2:
graph.graph(args.session, args.tmpfs, args.cache)
else:
graph.graph(sid, args.tmpfs, args.cache, cmd[1])
graph.graph(args.session, args.tmpfs, args.cache, args.command[1])
else:
fail(f"unknown command '{cmd[0]}'")
fail(f"unknown command '{args.command[0]}'")

407
watch.py

@ -7,29 +7,25 @@
import datetime
import fcntl
import os
import re
import select
import signal
import subprocess
import sys
import time
import psutil
import sched
import platform
import logging
from threading import Thread, Lock
import threading
import socket
import abc
import traceback
from threading import Thread
from logging.handlers import DatagramHandler
import graph
from common import *
die = 0
# Initialize summary variables
SAMPLE_NUMBER = 0
TOTAL_RAM = 0
@ -56,11 +52,6 @@ FS_SAR_INDEX = None
IFACE_NAME = None
IFACE_SAR_INDEX = None
# Handle SIGTERM
def kill_handler(a, b):
global die
die = 1
class UDPHandler(DatagramHandler):
def emit(self, msg):
try:
@ -70,10 +61,6 @@ class UDPHandler(DatagramHandler):
except Exception as e:
pass
logger = logging.getLogger("sargraph")
logger.setLevel(logging.INFO)
# Read a single table from sar output
def read_table(psar):
# Find the header
@ -114,74 +101,68 @@ def read_iface_stats(iface):
tx = scan(r"(\d+)", int, f.readline())
return rx, tx
def get_socket(session):
# TODO: when using on other platforms make sure this path exist; namely windows
path = f"/tmp/sargraph-{session}.sock"
sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
return sock, path
# Initialize 'data.txt' where the data is dumped
def initialize(session, machine):
global TOTAL_RAM
global TOTAL_GPU_RAM
with open("/proc/meminfo") as f:
TOTAL_RAM = int(scan("MemTotal:\s+(\d+)", float, f.read()))
class Watcher(abc.ABC):
uname = machine.split(" ")[0:2]
uname = f"{uname[0]} {uname[1]}"
def __init__(self, session, fsdev, iface, tmpfs_color, other_cache_color, udp=None, udp_cookie=None):
super().__init__()
cpus = int(machine.split(" CPU)")[0].split("(")[-1])
self.session = session
cpu_name = "unknown"
self.fsdev = fsdev
self.iface = iface
self.tmpfs_color = tmpfs_color
self.other_cache_color = other_cache_color
with open("/proc/cpuinfo") as f:
for line in f:
if "model name" in line:
cpu_name = line.replace("\n", "").split(": ")[1]
break
header = [
f"# sargraph version: {SARGRAPH_VERSION}",
f"pid: {os.getpid()}",
f"machine: {uname}",
f"cpu count: {cpus}",
f"cpu: {cpu_name}"
]
try:
pgpu = subprocess.run(
'nvidia-smi --query-gpu=name,driver_version,memory.total --format=csv,noheader,nounits'.split(' '),
capture_output=True
)
if pgpu.returncode == 0:
gpuname, gpudriver, memory_total = pgpu.stdout.decode('utf-8').rsplit(', ', 2)
header.extend([
f"gpu: {gpuname}",
f"gpu driver: {gpudriver}"
])
TOTAL_GPU_RAM = int(memory_total)
except Exception as e:
print(e)
pass
self.logger = logging.getLogger("sargraph")
self.logger.setLevel(logging.INFO)
logger.info(", ".join(header))
file_handler = logging.FileHandler(f"{session}.txt")
file_handler.setFormatter(logging.Formatter("%(message)s"))
self.logger.addHandler(file_handler)
def initialize_darwin(session):
global TOTAL_RAM
global TOTAL_GPU_RAM
self.socket, self.socket_path = get_socket(self.session)
TOTAL_RAM = int(psutil.virtual_memory().total / 1024)
# Was a graph alreay produced by save command from sargraph?
self.dont_plot = False
cpus = psutil.cpu_count(logical=True)
# Should we die?
self.die = False
cpu_name = platform.processor() or "unknown"
if udp is not None:
spl = udp.rsplit(':', 1)
udp_handler = UDPHandler(spl[0], int(spl[1]))
if udp_cookie is None:
udp_handler.setFormatter(logging.Formatter("%(message)s\n"))
else:
udp_handler.setFormatter(logging.Formatter(f"[{udp_cookie}] %(message)s\n"))
self.logger.addHandler(udp_handler)
header = [
f"# psutil version: {psutil.__version__}",
f"pid: {os.getpid()}",
f"machine: {platform.system()}",
f"cpu count: {cpus}",
f"cpu: {cpu_name}"
]
logger.info(", ".join(header))
# Initialize 'data.txt' where the data is dumped
@abc.abstractmethod
def initialize(self, machine):
pass
# Implement the watcher method
@abc.abstractmethod
def watch(self):
pass
def kill_handler(self, *_):
self.die = True
def recv_data(self) -> str:
data = self.socket.recv(1 << 10) # 1024 bytes should be enough
return data.decode("utf-8").replace("\n", "").strip()
# Add a summary comment to 'data.txt'
def summarize(session):
def summarize(self):
# Is there anything to be summarized?
if SAMPLE_NUMBER == 0:
return
@ -222,11 +203,11 @@ def summarize(session):
f"average gpu load: {TOTAL_GPU_LOAD / SAMPLE_NUMBER:.2f} %"
])
logger.info(", ".join([str(i) for i in summary]))
self.logger.info(", ".join([str(i) for i in summary]))
def get_meminfo(scheduler):
def get_meminfo(self, scheduler):
global MAX_USED_RAM
scheduler.enter(0.1, 1, get_meminfo, (scheduler,))
scheduler.enter(0.1, 1, self.get_meminfo, (scheduler,))
now = datetime.datetime.now()
date = now.strftime("%Y-%m-%d")
daytime = now.strftime("%H:%M:%S.%f")
@ -251,29 +232,109 @@ def get_meminfo(scheduler):
100 * ram_data.shared / ram_data.total
]
msg = " ".join(["psu"]+[str(i) for i in line])
logger.info(msg)
self.logger.info(msg)
def start(self):
self.socket.bind(self.socket_path)
signal.signal(signal.SIGTERM, self.kill_handler)
def watch(session, fsdev, iface, tmpfs_color, other_cache_color, use_psutil, udp=None, udp_cookie=None):
file_handler = logging.FileHandler(f"{session}.txt")
file_handler.setFormatter(logging.Formatter("%(message)s"))
logger.addHandler(file_handler)
try:
self.watch()
except Exception as e:
# make sure we prepend '#' to every line, to make reading file work
self.logger.error("# Exception while watching!")
for line in traceback.format_exception(type(e), e, e.__traceback__):
self.logger.error(f"# {line}")
try: # clean up after ourselves
os.unlink(self.socket_path)
except OSError:
pass
if udp is not None:
spl = udp.rsplit(':', 1)
udp_handler = UDPHandler(spl[0], int(spl[1]))
if udp_cookie is None:
udp_handler.setFormatter(logging.Formatter("%(message)s\n"))
return
def handle_command(self, label_line: str, s: sched.scheduler, now: datetime.datetime):
if label_line.startswith("command:"):
label_line = label_line[len("command:"):]
if label_line.startswith("q:"):
label_line = label_line[len("q:"):]
list(map(s.cancel, s.queue))
self.summarize()
if label_line == "none":
pass
elif label_line:
graph.graph(self.session, self.tmpfs_color, self.other_cache_color, label_line)
elif not self.dont_plot:
graph.graph(self.session, self.tmpfs_color, self.other_cache_color)
self.dont_plot = True
self.die = 1
return True
elif label_line.startswith("s:"):
label_line = label_line[len("s:"):]
self.dont_plot = True
if label_line != "none":
self.summarize()
if not label_line:
graph.graph(self.session, self.tmpfs_color, self.other_cache_color)
else:
udp_handler.setFormatter(logging.Formatter(f"[{udp_cookie}] %(message)s\n"))
logger.addHandler(udp_handler)
graph.graph(self.session, self.tmpfs_color, self.other_cache_color, label_line)
elif label_line.startswith('label:'):
label_line = label_line[len('label:'):]
with open(f"{self.session}.txt", "a") as f:
timestamp = now.strftime("%Y-%m-%d-%H:%M:%S")
print(f"# {timestamp} label: {label_line}", file=f)
return False
class SarWatcher(Watcher):
def initialize(self, machine):
global TOTAL_RAM
global TOTAL_GPU_RAM
with open("/proc/meminfo") as f:
TOTAL_RAM = int(scan("MemTotal:\s+(\d+)", float, f.read()))
uname = machine.split(" ")[0:2]
uname = f"{uname[0]} {uname[1]}"
cpus = int(machine.split(" CPU)")[0].split("(")[-1])
cpu_name = "unknown"
with open("/proc/cpuinfo") as f:
for line in f:
if "model name" in line:
cpu_name = line.replace("\n", "").split(": ")[1]
break
header = [
f"# sargraph version: {SARGRAPH_VERSION}",
f"pid: {os.getpid()}",
f"machine: {uname}",
f"cpu count: {cpus}",
f"cpu: {cpu_name}"
]
try:
pgpu = subprocess.run(
'nvidia-smi --query-gpu=name,driver_version,memory.total --format=csv,noheader,nounits'.split(' '),
capture_output=True
)
if pgpu.returncode == 0:
gpuname, gpudriver, memory_total = pgpu.stdout.decode('utf-8').rsplit(', ', 2)
header.extend([
f"gpu: {gpuname}",
f"gpu driver: {gpudriver}"
])
TOTAL_GPU_RAM = int(memory_total)
except Exception as e:
print(e)
pass
if is_darwin() or use_psutil:
return watch_psutil(session, fsdev, iface, tmpfs_color, other_cache_color)
return watch_sar(session, fsdev, iface, tmpfs_color, other_cache_color)
self.logger.info(", ".join(header))
# Run sar and gather data from it
def watch_sar(session, fsdev, iface, tmpfs_color, other_cache_color):
def watch(self):
global SAMPLE_NUMBER
global START_DATE
global END_DATE
@ -296,18 +357,13 @@ def watch_sar(session, fsdev, iface, tmpfs_color, other_cache_color):
global TOTAL_GPU_RAM
global MAX_USED_GPU_RAM
global die
# Was a graph alreay produced by save command from sargraph?
dont_plot = False
my_env = os.environ
my_env["S_TIME_FORMAT"] = "ISO"
psar = run_or_fail("sar", "-F", "-u", "-n", "DEV", "1", stdout=subprocess.PIPE, env=my_env)
s = sched.scheduler(time.time, time.sleep)
mem_ev = s.enter(0, 1, get_meminfo, (s,))
mem_ev = s.enter(0, 1, self.get_meminfo, (s,))
thread = Thread(target = s.run)
thread.start()
@ -322,32 +378,33 @@ def watch_sar(session, fsdev, iface, tmpfs_color, other_cache_color):
pgpu = None
machine = psar.stdout.readline().decode()
initialize(session, machine)
self.initialize(machine)
psar.stdout.readline()
signal.signal(signal.SIGTERM, kill_handler)
# Make stdin nonblocking to continue working when no command is sent
flags = fcntl.fcntl(sys.stdin, fcntl.F_GETFL)
fcntl.fcntl(sys.stdin, fcntl.F_SETFL, flags | os.O_NONBLOCK)
# Gather data from sar output
curr_gpu_util = 0
curr_gpu_mem = 0
socket_fd = self.socket.fileno()
while 1:
# Await sar output or a command sent from command handler in sargraph.py
readlist = [psar.stdout, sys.stdin]
readlist = [psar.stdout, socket_fd]
if pgpu:
readlist.append(pgpu.stdout)
rlist, _, _ = select.select(readlist, [], [], 0.25)
now = datetime.datetime.now()
if sys.stdin in rlist:
if handle_command(session, s, dont_plot, tmpfs_color, other_cache_color, now):
break
if psar.stdout not in rlist:
continue
if socket_fd in rlist:
data = self.recv_data()
now = datetime.datetime.now()
if self.handle_command(data, s, now):
break
date = now.strftime("%Y-%m-%d")
daytime = now.strftime("%H:%M:%S")
@ -365,8 +422,8 @@ def watch_sar(session, fsdev, iface, tmpfs_color, other_cache_color):
# Read and process network data
net_data = read_table(psar)
if IFACE_SAR_INDEX is None:
if iface:
IFACE_SAR_INDEX = net_data['IFACE'].index(iface)
if self.iface:
IFACE_SAR_INDEX = net_data['IFACE'].index(self.iface)
else:
maxj, maxv = 0, 0
for j, used in enumerate(net_data['IFACE']):
@ -387,8 +444,8 @@ def watch_sar(session, fsdev, iface, tmpfs_color, other_cache_color):
# Read and process FS data
fs_data = read_table(psar)
if FS_SAR_INDEX is None:
if fsdev:
FS_SAR_INDEX = fs_data['FILESYSTEM'].index(fsdev)
if self.fsdev:
FS_SAR_INDEX = fs_data['FILESYSTEM'].index(self.fsdev)
else:
maxj, maxv = 0, 0
for j, free in enumerate(fs_data['MBfsfree']):
@ -447,92 +504,42 @@ def watch_sar(session, fsdev, iface, tmpfs_color, other_cache_color):
f'{curr_gpu_util:.2f}',
f'{curr_gpu_mem / TOTAL_GPU_RAM * 100.0:.2f}'
])
logger.info(" ".join(["sar"]+[str(i) for i in line]))
self.logger.info(" ".join(["sar"]+[str(i) for i in line]))
if die:
if self.die:
break
list(map(s.cancel, s.queue))
thread.join()
# This runs if we were stopped by SIGTERM and no plot was made so far
if not dont_plot:
summarize(session)
graph.graph(session, tmpfs_color, other_cache_color)
if not self.dont_plot:
self.summarize()
graph.graph(self.session, self.tmpfs_color, self.other_cache_color)
def watch_psutil(session, fsdev, iface, tmpfs_color, other_cache_color):
# Was a graph already produced by save command from sargraph?
dont_plot = False
s = sched.scheduler(time.time, time.sleep)
sar_ev = s.enter(0, 1, psutil_sar_simulation, (s,))
mem_ev = s.enter(0, 1, get_meminfo, (s,))
thread = Thread(target = s.run)
thread.start()
initialize_darwin(session)
signal.signal(signal.SIGTERM, kill_handler)
class PsUtilWatcher(Watcher):
# Make stdin nonblocking to continue working when no command is sent
flags = fcntl.fcntl(sys.stdin, fcntl.F_GETFL)
fcntl.fcntl(sys.stdin, fcntl.F_SETFL, flags | os.O_NONBLOCK)
while 1:
# Await sar output or a command sent from command handler in sargraph.py
readlist = [sys.stdin]
rlist, _, _ = select.select(readlist, [], [], 0.25)
now = datetime.datetime.now()
if handle_command(session, s, dont_plot, tmpfs_color, other_cache_color, now):
break
list(map(s.cancel, s.queue))
thread.join()
# This runs if we were stopped by SIGTERM and no plot was made so far
if not dont_plot:
summarize(session)
graph.graph(session, tmpfs_color, other_cache_color)
def initialize(self, _ = None):
global TOTAL_RAM
global TOTAL_GPU_RAM
def handle_command(session, s, dont_plot, tmpfs_color, other_cache_color, now):
global die
label_line = sys.stdin.readline().replace("\n", "")
if label_line.startswith("command:"):
label_line = label_line[len("command:"):]
if label_line.startswith("q:"):
label_line = label_line[len("q:"):]
TOTAL_RAM = int(psutil.virtual_memory().total / 1024)
list(map(s.cancel, s.queue))
summarize(session)
if label_line == "none":
pass
elif label_line:
graph.graph(session, tmpfs_color, other_cache_color, label_line)
elif not dont_plot:
graph.graph(session, tmpfs_color, other_cache_color)
dont_plot = True
die = 1
return True
elif label_line.startswith("s:"):
label_line = label_line[len("s:"):]
cpus = psutil.cpu_count(logical=True)
dont_plot = True
cpu_name = platform.processor() or "unknown"
if label_line != "none":
summarize(session)
if not label_line:
graph.graph(session, tmpfs_color, other_cache_color)
else:
graph.graph(session, tmpfs_color, other_cache_color, label_line)
elif label_line.startswith('label:'):
label_line = label_line[len('label:'):]
with open(f"{session}.txt", "a") as f:
timestamp = now.strftime("%Y-%m-%d-%H:%M:%S")
print(f"# {timestamp} label: {label_line}", file=f)
return False
header = [
f"# psutil version: {psutil.__version__}",
f"pid: {os.getpid()}",
f"machine: {platform.system()}",
f"cpu count: {cpus}",
f"cpu: {cpu_name}"
]
self.logger.info(", ".join(header))
# sar is not available on macOS. This function creates the sar behavior, but use psutil instead.
def psutil_sar_simulation(scheduler):
def psutil_sar_simulation(self, scheduler: sched.scheduler):
global START_DATE
global TOTAL_LOAD
global SAMPLE_NUMBER
@ -549,7 +556,7 @@ def psutil_sar_simulation(scheduler):
global FS_NAME
global END_DATE
scheduler.enter(1, 1, psutil_sar_simulation, (scheduler,))
scheduler.enter(1, 1, self.psutil_sar_simulation, (scheduler,))
now = datetime.datetime.now()
date = now.strftime("%Y-%m-%d")
daytime = now.strftime("%H:%M:%S")
@ -599,4 +606,36 @@ def psutil_sar_simulation(scheduler):
curr_tx / 128,
]
logger.info(" ".join(["sar"]+[str(i) for i in line]))
self.logger.info(" ".join(["sar"]+[str(i) for i in line]))
def watch(self):
# Was a graph already produced by save command from sargraph?
dont_plot = False
s = sched.scheduler(time.time, time.sleep)
sar_ev = s.enter(0, 1, self.psutil_sar_simulation, (s,))
mem_ev = s.enter(0, 1, self.get_meminfo, (s,))
thread = Thread(target = s.run)
thread.start()
self.initialize()
socket_fd = self.socket.fileno()
while 1:
# Await sar output or a command sent from command handler in sargraph.py
readlist = [socket_fd]
rlist, _, _ = select.select(readlist, [], [], 0.25)
if socket_fd in rlist:
data = self.recv_data()
now = datetime.datetime.now()
if self.handle_command(data, s, now):
break
list(map(s.cancel, s.queue))
thread.join()
# This runs if we were stopped by SIGTERM and no plot was made so far
if not dont_plot:
self.summarize()
graph.graph(self.session, self.tmpfs_color, self.other_cache_color)
Loading…
Cancel
Save