diff --git a/README.md b/README.md
index 9a96bc42f1..5cf675fa50 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,7 @@ Slips v1.1.7
[](https://www.stratosphereips.org/blog/tag/slips)
[](https://discord.gg/zu5HwMFy5C)

+
diff --git a/config/slips.yaml b/config/slips.yaml
index 34f41e7109..08a745f309 100644
--- a/config/slips.yaml
+++ b/config/slips.yaml
@@ -455,7 +455,8 @@ Docker:
#############################
Profiling:
# CPU profiling
- # enable cpu profiling [yes,no]
+ # enable cpu profiling [true/false]
+ # NOTE: the cpu profiler uses port 9001 to show the results.
cpu_profiler_enable: false
# Available options are [dev,live]
@@ -466,11 +467,17 @@ Profiling:
# time. it is accessible from web interface
cpu_profiler_mode: dev
- # profile all subprocesses in dev mode [yes,no].
- cpu_profiler_multiprocess: true
+ # decides whether the profiler tracks all processes or only one.
+ # only used in dev mode [true,false].
+ cpu_profiler_multiprocess: false
# set number of tracer entries (dev mode only)
- cpu_profiler_dev_mode_entries: 1000000
+ # VizTracer uses a circular buffer to store the entries.
+ # When there are too many entries, it will only store the latest ones
+ # so you know what happened recently.
+ # the more the entries, the more RAM viztracer is going to use.
+ # https://viztracer.readthedocs.io/en/latest/basic_usage.html#circular-buffer-size
+ cpu_profiler_dev_mode_entries: 500000
# set maximum output lines (live mode only)
cpu_profiler_output_limit: 20
@@ -478,13 +485,13 @@ Profiling:
# set the wait time between sampling sequences in seconds (live mode only)
cpu_profiler_sampling_interval: 20
- # enable memory profiling [yes,no]
+ # enable memory profiling [true,false]
memory_profiler_enable: false
# set profiling mode [dev,live]
memory_profiler_mode: live
- # profile all subprocesses [yes,no]
+ # profile all subprocesses [true,false]
memory_profiler_multiprocess: true
#############################
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 4cc486fda5..04e9672a9a 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -30,7 +30,6 @@ ENV NVM_DIR=/root/.nvm
SHELL ["/bin/bash", "-c"]
-# Install wget and add Zeek and redis repositories to our sources.
RUN apt update && apt install -y --no-install-recommends \
wget \
ca-certificates \
diff --git a/docs/profiling_slips.md b/docs/profiling_slips.md
index fc4d930887..b1ab9ce6bd 100644
--- a/docs/profiling_slips.md
+++ b/docs/profiling_slips.md
@@ -85,7 +85,7 @@ Use WASD to zoom in and move left/right.
### CPU Profiler Live Mode
#### Step 1
Go to slips.yaml and make sure the settings are set correctly.
-```cpu_profiler_enable = yes```
+```cpu_profiler_enable = True```
```cpu_profiler_mode = live```
You can also set maximum output lines (live mode only) to adjust profiler behavior.
@@ -112,7 +112,7 @@ If we print out the data getting sent to the “cpu_profile” redis channel, it
The memory profiler settings are much simpler.
in slips.yaml, first, enable memory profiling
-```memory_profiler_enable = yes```
+```memory_profiler_enable = True```
and set profiling mode:
@@ -120,7 +120,7 @@ and set profiling mode:
```memory_profiler_mode = dev```
now, profile all subprocesses
-```memory_profiler_multiprocess = yes```
+```memory_profiler_multiprocess = True```
#### Step 2
@@ -158,9 +158,9 @@ Under the table directory, the files are much simpler. They just show a table of
Go to ```slips.yaml``` and use the following settings
```
-memory_profiler_enable = yes
-memory_profiler_mode = yes
-memory_profiler_multiprocess = yes
+memory_profiler_enable = True
+memory_profiler_mode = True
+memory_profiler_multiprocess = True
```
### Step 2
diff --git a/managers/process_manager.py b/managers/process_manager.py
index 4499b5ac04..4f685b764d 100644
--- a/managers/process_manager.py
+++ b/managers/process_manager.py
@@ -721,9 +721,6 @@ def shutdown_gracefully(self):
format_ = self.main.conf.export_labeled_flows_to().lower()
self.main.db.export_labeled_flows(format_)
- self.main.profilers_manager.cpu_profiler_release()
- self.main.profilers_manager.memory_profiler_release()
-
# if store_a_copy_of_zeek_files is set to yes in slips.yaml
# copy the whole zeek_files dir to the output dir
self.main.store_zeek_dir_copy()
@@ -740,6 +737,9 @@ def shutdown_gracefully(self):
f"finished in {analysis_time:.2f} minutes"
)
+ self.main.profilers_manager.cpu_profiler_release()
+ self.main.profilers_manager.memory_profiler_release()
+
self.main.db.close()
if graceful_shutdown:
print(
diff --git a/managers/profilers_manager.py b/managers/profilers_manager.py
index ff29a0c1d2..97a3893c3e 100644
--- a/managers/profilers_manager.py
+++ b/managers/profilers_manager.py
@@ -3,11 +3,13 @@
import os
import subprocess
import sys
+from slips_files.common.style import green
class ProfilersManager:
def __init__(self, main):
self.main = main
+ self.args = self.main.args
self.read_configurations()
def read_configurations(self):
@@ -50,28 +52,38 @@ def cpu_profiler_init(self):
args = sys.argv
if args[-1] != "--no-recurse":
tracer_entries = str(self.cpu_profiler_dev_mode_entries)
+ output_file = str(
+ os.path.join(
+ self.args.output,
+ "cpu_profiling_result.json",
+ )
+ )
viz_args = [
"viztracer",
"--tracer_entries",
tracer_entries,
"--max_stack_depth",
- "10",
+ "5",
"-o",
- str(
- os.path.join(
- self.args.output,
- "cpu_profiling_result.json",
- )
- ),
+ output_file,
+ # viztracer takes -- as a separator between arguments
+ # to viztracer and positional arguments to your script.
+ "--",
]
+ # add slips args
viz_args.extend(args)
+ # add --no-recurse to avoid infinite recursion
viz_args.append("--no-recurse")
print(
- "Starting multiprocess profiling recursive subprocess"
+ f"Starting multiprocess profiling recursive "
+ f"subprocess using command: "
+ f"{green(' '.join(viz_args))}"
)
subprocess.run(viz_args)
exit(0)
else:
+ # reaching here means slips is now running using the vistracer
+ # command
self.cpu_profiler = CPUProfiler(
db=self.main.db,
output=self.args.output,
diff --git a/slips/main.py b/slips/main.py
index 2206741b65..9fb562a928 100644
--- a/slips/main.py
+++ b/slips/main.py
@@ -45,7 +45,6 @@ def __init__(self, testing=False):
self.conf = ConfigParser()
self.metadata_man = MetadataManager(self)
self.ui_man = UIManager(self)
- self.profilers_manager = ProfilersManager(self)
self.version = utils.get_slips_version()
# will be filled later
self.commit = "None"
@@ -57,6 +56,7 @@ def __init__(self, testing=False):
# TODO use mocks instead of this testing param
if not testing:
self.args = self.conf.get_args()
+ self.profilers_manager = ProfilersManager(self)
self.pid = os.getpid()
self.checker.check_given_flags()
diff --git a/slips_files/common/performance_profilers/cpu_profiler.py b/slips_files/common/performance_profilers/cpu_profiler.py
index 7b825a5a2f..858dd3e538 100644
--- a/slips_files/common/performance_profilers/cpu_profiler.py
+++ b/slips_files/common/performance_profilers/cpu_profiler.py
@@ -15,6 +15,9 @@
class CPUProfiler(IPerformanceProfiler):
def __init__(self, db, output, mode="dev", limit=20, interval=20):
+ """
+ :param output: the currently used slips output directory
+ """
valid_modes = ["dev", "live"]
if mode not in valid_modes:
print(
@@ -46,6 +49,9 @@ def print(self):
class DevProfiler(IPerformanceProfiler):
def __init__(self, output):
+ """
+ :param output: the currently used slips output directory
+ """
self.profiler = self._create_profiler()
self.output = output
@@ -59,8 +65,12 @@ def stop(self):
self.profiler.stop()
def print(self):
+ """
+ Prints the profiling result to cpu_profiling_result in slips output
+ directory
+ """
result_path = os.path.join(self.output, "cpu_profiling_result.json")
- self.profiler.save(result_path)
+ self.profiler.save(output_file=result_path)
class LiveProfiler(IPerformanceProfiler):
diff --git a/slips_files/core/database/sqlite_db/database.py b/slips_files/core/database/sqlite_db/database.py
index d2360ea073..fe39ca8f4a 100644
--- a/slips_files/core/database/sqlite_db/database.py
+++ b/slips_files/core/database/sqlite_db/database.py
@@ -434,12 +434,3 @@ def execute(self, query: str, params=None) -> None:
elif "database is locked" in str(err):
sleep(5)
- else:
- self.print(
- f"Re-trying to execute query "
- f"({query} - {params}) - {err}. "
- f"Trial number: {trial}.",
- 0,
- 1,
- log_to_logfiles_only=True,
- )
diff --git a/tests/module_factory.py b/tests/module_factory.py
index 6c14547ee9..cda65bfaf5 100644
--- a/tests/module_factory.py
+++ b/tests/module_factory.py
@@ -12,6 +12,7 @@
from managers.host_ip_manager import HostIPManager
from managers.metadata_manager import MetadataManager
+from managers.profilers_manager import ProfilersManager
from modules.flowalerts.conn import Conn
from modules.threat_intelligence.circl_lu import Circllu
from modules.threat_intelligence.spamhaus import Spamhaus
@@ -364,6 +365,13 @@ def create_redis_manager_obj(self, mock_db):
main.args = Mock()
return RedisManager(main)
+ @patch(MODULE_DB_MANAGER, name="mock_db")
+ def create_profilers_manager_obj(self, mock_db):
+ main = self.create_main_obj()
+ main.db = mock_db
+ main.args = Mock()
+ return ProfilersManager(main)
+
@patch(MODULE_DB_MANAGER, name="mock_db")
def create_host_ip_manager_obj(self, mock_db):
main = self.create_main_obj()
diff --git a/tests/test_main.py b/tests/test_main.py
index f39e72ea07..571cebd506 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -60,60 +60,6 @@ def test_is_total_flows_unknown(args, input_type, expected_result):
assert main.is_total_flows_unknown() == expected_result
-@pytest.mark.parametrize(
- "cpu_profiler_multiprocess, expected_stop_calls, expected_print_calls",
- [ # Testcase1: CPU profiler enabled, not multiprocess
- (False, 1, 1),
- # Testcase2: CPU profiler enabled, multiprocess
- (True, 0, 0),
- ],
-)
-def test_cpu_profiler_release_enabled(
- cpu_profiler_multiprocess,
- expected_stop_calls,
- expected_print_calls,
-):
- main = ModuleFactory().create_main_obj()
- main.profilers_manager.cpu_profiler_enabled = True
- main.profilers_manager.cpu_profiler_multiprocess = (
- cpu_profiler_multiprocess
- )
- main.profilers_manager.cpu_profiler = MagicMock()
- main.profilers_manager.cpu_profiler_release()
-
- assert (
- main.profilers_manager.cpu_profiler.stop.call_count
- == expected_stop_calls
- )
- assert (
- main.profilers_manager.cpu_profiler.print.call_count
- == expected_print_calls
- )
-
-
-def test_cpu_profiler_release_disabled():
- main = ModuleFactory().create_main_obj()
- main.profilers_manager.cpu_profiler_enabled = False
- main.profilers_manager.cpu_profiler_release()
- assert not hasattr(main.profilers_manager, "memory_profiler")
-
-
-def test_memory_profiler_release_enabled():
- main = ModuleFactory().create_main_obj()
- main.profilers_manager.memory_profiler_enabled = True
- main.profilers_manager.memory_profiler = MagicMock()
- main.profilers_manager.memory_profiler_release()
- main.profilers_manager.memory_profiler.stop.assert_called_once()
-
-
-def test_memory_profiler_release_disabled():
- main = ModuleFactory().create_main_obj()
- main.profilers_manager.memory_profiler_enabled = False
- main.profilers_manager.memory_profiler_release()
-
- assert not hasattr(main.profilers_manager, "memory_profiler")
-
-
@pytest.mark.parametrize(
"mode, time_diff, expected_calls",
[ # Testcase1: Should update stats
diff --git a/tests/test_profilers_manager.py b/tests/test_profilers_manager.py
new file mode 100644
index 0000000000..83a241cc1d
--- /dev/null
+++ b/tests/test_profilers_manager.py
@@ -0,0 +1,51 @@
+# SPDX-FileCopyrightText: 2021 Sebastian Garcia
+# SPDX-License-Identifier: GPL-2.0-only
+from unittest.mock import MagicMock
+import pytest
+from tests.module_factory import ModuleFactory
+
+
+@pytest.mark.parametrize(
+ "cpu_profiler_multiprocess, expected_stop_calls, expected_print_calls",
+ [ # Testcase1: CPU profiler enabled, not multiprocess
+ (False, 1, 1),
+ # Testcase2: CPU profiler enabled, multiprocess
+ (True, 0, 0),
+ ],
+)
+def test_cpu_profiler_release_enabled(
+ cpu_profiler_multiprocess,
+ expected_stop_calls,
+ expected_print_calls,
+):
+ handler = ModuleFactory().create_profilers_manager_obj()
+ handler.cpu_profiler_enabled = True
+ handler.cpu_profiler_multiprocess = cpu_profiler_multiprocess
+ handler.cpu_profiler = MagicMock()
+ handler.cpu_profiler_release()
+
+ assert handler.cpu_profiler.stop.call_count == expected_stop_calls
+ assert handler.cpu_profiler.print.call_count == expected_print_calls
+
+
+def test_cpu_profiler_release_disabled():
+ handler = ModuleFactory().create_profilers_manager_obj()
+ handler.cpu_profiler_enabled = False
+ handler.cpu_profiler_release()
+ assert not hasattr(handler, "memory_profiler")
+
+
+def test_memory_profiler_release_enabled():
+ handler = ModuleFactory().create_profilers_manager_obj()
+ handler.memory_profiler_enabled = True
+ handler.memory_profiler = MagicMock()
+ handler.memory_profiler_release()
+ handler.memory_profiler.stop.assert_called_once()
+
+
+def test_memory_profiler_release_disabled():
+ handler = ModuleFactory().create_profilers_manager_obj()
+ handler.memory_profiler_enabled = False
+ handler.memory_profiler_release()
+
+ assert not hasattr(handler, "memory_profiler")