233 lines
7.8 KiB
Diff
233 lines
7.8 KiB
Diff
From e8fb537645141bc87425b481e48f8484f05725b8 Mon Sep 17 00:00:00 2001
|
|
From: hubin <hubin73@huawei.com>
|
|
Date: Sat, 15 Jul 2023 12:17:36 +0800
|
|
Subject: [PATCH] tuned: add app-sensor profile
|
|
|
|
add application plugin and process monitor support for app-sensor profile.
|
|
|
|
if switch to app-sensor, apps given in app-sensor conf file will be
|
|
monitored, and actions given repectively in app-sensor conf file will
|
|
be executed when the monitored app starts or quits.
|
|
|
|
Signed-off-by: hubin <hubin73@huawei.com>
|
|
---
|
|
profiles/app-sensor/tuned.conf | 23 ++++++++
|
|
tuned/monitors/monitor_process.py | 83 ++++++++++++++++++++++++++++
|
|
tuned/plugins/plugin_application.py | 84 +++++++++++++++++++++++++++++
|
|
3 files changed, 190 insertions(+)
|
|
create mode 100644 profiles/app-sensor/tuned.conf
|
|
create mode 100644 tuned/monitors/monitor_process.py
|
|
create mode 100644 tuned/plugins/plugin_application.py
|
|
|
|
diff --git a/profiles/app-sensor/tuned.conf b/profiles/app-sensor/tuned.conf
|
|
new file mode 100644
|
|
index 0000000..771e6ae
|
|
--- /dev/null
|
|
+++ b/profiles/app-sensor/tuned.conf
|
|
@@ -0,0 +1,23 @@
|
|
+[main]
|
|
+summary=dynamic tuning for configured apps
|
|
+
|
|
+[application]
|
|
+# Define list of monitored apps, separated by comma.
|
|
+# Only apps declared in name will be monitored and execute defined action when app starts and quits.
|
|
+# Definition syntax:
|
|
+# name={app1},{app2},...
|
|
+# for example:
|
|
+# name=redis,mysql
|
|
+name=
|
|
+
|
|
+# Define action or rollback action for each monitored app.
|
|
+# No definition or blank action means no action.
|
|
+# Definition syntax:
|
|
+# {app}_action={command}
|
|
+# {app}_rollback_action={command}
|
|
+# for example:
|
|
+# redis_action="sysctl -w kernel.sched_min_granularity_ns=10000000"
|
|
+# redis_rollback_action="sysctl -w kernel.sched_min_granularity_ns=3000000"
|
|
+# mysql_action=
|
|
+# mysql_rollback_action=
|
|
+
|
|
diff --git a/tuned/monitors/monitor_process.py b/tuned/monitors/monitor_process.py
|
|
new file mode 100644
|
|
index 0000000..524b27a
|
|
--- /dev/null
|
|
+++ b/tuned/monitors/monitor_process.py
|
|
@@ -0,0 +1,83 @@
|
|
+import psutil
|
|
+import tuned.monitors
|
|
+import tuned.logs
|
|
+
|
|
+log = tuned.logs.get()
|
|
+
|
|
+
|
|
+class ProcessMonitor(tuned.monitors.Monitor):
|
|
+ app_program_dict = {
|
|
+ "mysql": ["mysqld"],
|
|
+ "redis": ["redis-server"],
|
|
+ "nginx": ["nginx"],
|
|
+ "unixbench": ["Run"],
|
|
+ "unixbench-arithoh": ["arithoh"],
|
|
+ "unixbench-context1": ["context1"],
|
|
+ "unixbench-dhry2": ["dhry2"],
|
|
+ "unixbench-dhry2reg": ["dhry2reg"],
|
|
+ "unixbench-double": ["double"],
|
|
+ "unixbench-execl": ["execl"],
|
|
+ "unixbench-float": ["float"],
|
|
+ "unixbench-fstime": ["fstime"],
|
|
+ "unixbench-gfx-x11": ["gfx-x11"],
|
|
+ "unixbench-hanoi": ["hanoi"],
|
|
+ "unixbench-int": ["int"],
|
|
+ "unixbench-long": ["long"],
|
|
+ "unixbench-looper": ["looper"],
|
|
+ "unixbench-pipe": ["pipe"],
|
|
+ "unixbench-register": ["register"],
|
|
+ "unixbench-short": ["short"],
|
|
+ "unixbench-spawn": ["spawn"],
|
|
+ "unixbench-syscall": ["syscall"],
|
|
+ "unixbench-whetstone-double": ["whetstone-double"],
|
|
+ "fio": ["fio"],
|
|
+ "iozone": ["iozone"],
|
|
+ "lmbench": ["lmbench"],
|
|
+ "netperf": ["netperf"]
|
|
+ }
|
|
+
|
|
+ pid_set = set()
|
|
+ pid_app_dict = {}
|
|
+
|
|
+ @classmethod
|
|
+ def _init_available_devices(cls):
|
|
+ cls._available_devices = set(["application"])
|
|
+ cls._load["application"] = set()
|
|
+
|
|
+ @classmethod
|
|
+ def update(cls):
|
|
+ cur_pids = set(psutil.pids())
|
|
+ prev_pids = cls.pid_set
|
|
+
|
|
+ # collect new pid and gone pid
|
|
+ new_pids = cur_pids - prev_pids
|
|
+ gone_pids = prev_pids - cur_pids
|
|
+
|
|
+ # deal with gone pids
|
|
+ if len(gone_pids) > 0:
|
|
+ log.debug(f"find {len(gone_pids)} processes gone")
|
|
+ for pid in gone_pids:
|
|
+ cls.pid_set.remove(pid)
|
|
+ if pid in cls.pid_app_dict:
|
|
+ log.debug(f"app process gone: {cls.pid_app_dict[pid]} (pid {pid})")
|
|
+ cls.pid_app_dict.pop(pid)
|
|
+
|
|
+ # deal with new pids
|
|
+ if len(new_pids) > 0:
|
|
+ log.debug(f"find {len(new_pids)} processes created")
|
|
+ for pid in new_pids:
|
|
+ try:
|
|
+ process = psutil.Process(pid)
|
|
+ process_name = process.name()
|
|
+ except psutil.NoSuchProcess:
|
|
+ continue
|
|
+ cls.pid_set.add(pid)
|
|
+ # match process name with known applications
|
|
+ for app in cls.app_program_dict:
|
|
+ if process_name in cls.app_program_dict[app]:
|
|
+ cls.pid_app_dict[pid] = app
|
|
+ log.debug(f"app process created: {cls.pid_app_dict[pid]} (pid {pid})")
|
|
+ break
|
|
+
|
|
+ # output current running applications
|
|
+ cls._load["application"] = set(cls.pid_app_dict.values())
|
|
diff --git a/tuned/plugins/plugin_application.py b/tuned/plugins/plugin_application.py
|
|
new file mode 100644
|
|
index 0000000..946d284
|
|
--- /dev/null
|
|
+++ b/tuned/plugins/plugin_application.py
|
|
@@ -0,0 +1,84 @@
|
|
+import subprocess
|
|
+from . import base
|
|
+from . import exceptions
|
|
+import tuned.logs
|
|
+from tuned.utils.commands import commands
|
|
+
|
|
+log = tuned.logs.get()
|
|
+
|
|
+ACTION_TIMEOUT = 180
|
|
+
|
|
+class ApplicationPlugin(base.Plugin):
|
|
+ """
|
|
+ `application`:
|
|
+
|
|
+ Dynamically executes the optimization action according to the application
|
|
+ running situation.
|
|
+ """
|
|
+ last_apps_running = set()
|
|
+
|
|
+ def __init__(self, *args, **kwargs):
|
|
+ super(ApplicationPlugin, self).__init__(*args, **kwargs)
|
|
+ self._has_dynamic_options = True
|
|
+
|
|
+ def _instance_init(self, instance):
|
|
+ instance._has_static_tuning = False
|
|
+ instance._has_dynamic_tuning = True
|
|
+ # save all options
|
|
+ instance.option_dict = {}
|
|
+ for option, value in list(instance.options.items()):
|
|
+ instance.option_dict[option] = value
|
|
+ log.debug(f"{option}: {value}")
|
|
+ instance._load_monitor = self._monitors_repository.create("process", None)
|
|
+
|
|
+ def _instance_cleanup(self, instance):
|
|
+ if instance._load_monitor is not None:
|
|
+ self._monitors_repository.delete(instance._load_monitor)
|
|
+ instance._load_monitor = None
|
|
+ instance.option_dict.clear()
|
|
+
|
|
+ def _instance_update_dynamic(self, instance, device):
|
|
+ if "name" not in instance.option_dict:
|
|
+ return
|
|
+ applications_monitored = set([app.strip() for app in instance.option_dict["name"].split(',')])
|
|
+ if not applications_monitored:
|
|
+ return
|
|
+ apps_running = applications_monitored.intersection(instance._load_monitor.get_load()["application"])
|
|
+ log.debug("running: " + str(apps_running))
|
|
+ new_apps = apps_running - self.last_apps_running
|
|
+ gone_apps = self.last_apps_running - apps_running
|
|
+ if len(new_apps) > 0:
|
|
+ log.info("new apps: " + str(new_apps))
|
|
+ if len(gone_apps) > 0:
|
|
+ log.info("gone apps: " + str(gone_apps))
|
|
+ for app in gone_apps:
|
|
+ self._execute_action(instance, app, rollback=True)
|
|
+ for app in new_apps:
|
|
+ self._execute_action(instance, app, rollback=False)
|
|
+ self.last_apps_running = apps_running
|
|
+
|
|
+ def _instance_unapply_dynamic(self, instance, device):
|
|
+ # restore previous value
|
|
+ pass
|
|
+
|
|
+ def _execute_action(self, instance, app, rollback=False):
|
|
+ # find action
|
|
+ if rollback:
|
|
+ option = f"{app}_rollback_action"
|
|
+ else:
|
|
+ option = f"{app}_action"
|
|
+ if option not in instance.option_dict:
|
|
+ return
|
|
+ action = str(instance.option_dict[option])
|
|
+ # remove wrapping " or ' in action
|
|
+ if len(action) >= 2 and (action[0] == '"' or action[0] == "'") and action[0] == action[-1]:
|
|
+ action = action[1:-1]
|
|
+ # execute action for app
|
|
+ if len(action):
|
|
+ log.info(f"{option}: {action}")
|
|
+ try:
|
|
+ p = subprocess.Popen(action, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
+ retval = p.wait(ACTION_TIMEOUT)
|
|
+ log.info(f"{option}: return {retval}")
|
|
+ except Exception as e:
|
|
+ log.info(f"{option}: {str(e)}")
|
|
--
|
|
2.33.0
|
|
|