pg-lock-tracer 0.6.1__tar.gz → 0.7.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. {pg_lock_tracer-0.6.1 → pg_lock_tracer-0.7.1}/PKG-INFO +63 -11
  2. pg_lock_tracer-0.6.1/src/pg_lock_tracer.egg-info/PKG-INFO → pg_lock_tracer-0.7.1/README.md +54 -25
  3. pg_lock_tracer-0.7.1/pyproject.toml +62 -0
  4. pg_lock_tracer-0.7.1/setup.cfg +4 -0
  5. pg_lock_tracer-0.7.1/src/pg_lock_tracer/__init__.py +1 -0
  6. pg_lock_tracer-0.7.1/src/pg_lock_tracer/bpf/pg_spinlock_delay_tracer.c +57 -0
  7. {pg_lock_tracer-0.6.1 → pg_lock_tracer-0.7.1}/src/pg_lock_tracer/oid_resolver.py +1 -1
  8. {pg_lock_tracer-0.6.1 → pg_lock_tracer-0.7.1}/src/pg_lock_tracer/pg_lock_tracer.py +7 -2
  9. pg_lock_tracer-0.7.1/src/pg_lock_tracer/pg_spinlock_delay_tracer.py +159 -0
  10. pg_lock_tracer-0.6.1/README.md → pg_lock_tracer-0.7.1/src/pg_lock_tracer.egg-info/PKG-INFO +77 -3
  11. {pg_lock_tracer-0.6.1 → pg_lock_tracer-0.7.1}/src/pg_lock_tracer.egg-info/SOURCES.txt +2 -1
  12. {pg_lock_tracer-0.6.1 → pg_lock_tracer-0.7.1}/src/pg_lock_tracer.egg-info/entry_points.txt +1 -0
  13. pg_lock_tracer-0.6.1/pyproject.toml +0 -3
  14. pg_lock_tracer-0.6.1/setup.cfg +0 -57
  15. pg_lock_tracer-0.6.1/src/pg_lock_tracer/__init__.py +0 -1
  16. {pg_lock_tracer-0.6.1 → pg_lock_tracer-0.7.1}/LICENSE +0 -0
  17. {pg_lock_tracer-0.6.1 → pg_lock_tracer-0.7.1}/setup.py +0 -0
  18. {pg_lock_tracer-0.6.1 → pg_lock_tracer-0.7.1}/src/pg_lock_tracer/animate_lock_graph.py +0 -0
  19. {pg_lock_tracer-0.6.1 → pg_lock_tracer-0.7.1}/src/pg_lock_tracer/bpf/__init__.py +0 -0
  20. {pg_lock_tracer-0.6.1 → pg_lock_tracer-0.7.1}/src/pg_lock_tracer/bpf/pg_lock_tracer.c +0 -0
  21. {pg_lock_tracer-0.6.1 → pg_lock_tracer-0.7.1}/src/pg_lock_tracer/bpf/pg_lw_lock_tracer.c +0 -0
  22. {pg_lock_tracer-0.6.1 → pg_lock_tracer-0.7.1}/src/pg_lock_tracer/bpf/pg_row_lock_tracer.c +0 -0
  23. {pg_lock_tracer-0.6.1 → pg_lock_tracer-0.7.1}/src/pg_lock_tracer/helper.py +0 -0
  24. {pg_lock_tracer-0.6.1 → pg_lock_tracer-0.7.1}/src/pg_lock_tracer/pg_lw_lock_tracer.py +0 -0
  25. {pg_lock_tracer-0.6.1 → pg_lock_tracer-0.7.1}/src/pg_lock_tracer/pg_row_lock_tracer.py +0 -0
  26. {pg_lock_tracer-0.6.1 → pg_lock_tracer-0.7.1}/src/pg_lock_tracer.egg-info/dependency_links.txt +0 -0
  27. {pg_lock_tracer-0.6.1 → pg_lock_tracer-0.7.1}/src/pg_lock_tracer.egg-info/requires.txt +0 -0
  28. {pg_lock_tracer-0.6.1 → pg_lock_tracer-0.7.1}/src/pg_lock_tracer.egg-info/top_level.txt +0 -0
  29. {pg_lock_tracer-0.6.1 → pg_lock_tracer-0.7.1}/tests/test_helper.py +0 -0
@@ -1,24 +1,25 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: pg_lock_tracer
3
- Version: 0.6.1
4
- Summary: A BPF based lock tracer for the PostgreSQL database
5
- Home-page: https://github.com/jnidzwetzki/pg-lock-tracer
6
- Author: Jan Nidzwetzki
3
+ Version: 0.7.1
4
+ Summary: An eBPF based lock tracer for PostgreSQL
5
+ Author-email: Jan Nidzwetzki <jnidzwetzki@gmx.de>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/jnidzwetzki/pg-lock-tracer
7
8
  Project-URL: Bug Tracker, https://github.com/jnidzwetzki/pg-lock-tracer/issues
8
- Keywords: postgresql bpf lock locktracer
9
+ Keywords: postgresql,postgres,ebpf,locktracer
9
10
  Classifier: Development Status :: 4 - Beta
10
11
  Classifier: Intended Audience :: Developers
11
12
  Classifier: Operating System :: POSIX :: Linux
12
13
  Classifier: Programming Language :: Python
13
14
  Classifier: Topic :: Software Development :: Debuggers
14
- Classifier: License :: OSI Approved :: Apache Software License
15
- Requires-Python: >=3.6
15
+ Requires-Python: >=3.10
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
18
  Requires-Dist: graphviz
19
19
  Requires-Dist: igraph
20
20
  Requires-Dist: prettytable
21
21
  Requires-Dist: psycopg2
22
+ Dynamic: license-file
22
23
 
23
24
  # Lock tracing tools for PostgreSQL
24
25
  [![Make a PR](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com)
@@ -33,9 +34,10 @@ This project provides tools that allow you to gain deep insights into PostgreSQL
33
34
  * `pg_lock_tracer` - is a PostgreSQL table level lock tracer.
34
35
  * `pg_lw_lock_tracer` - is a tracer for PostgreSQL lightweight locks (LWLocks).
35
36
  * `pg_row_lock_tracer` - is a tracer for PostgreSQL row locks.
37
+ * `pg_spinlock_delay_tracer` - is a tracer for PostgreSQL spinlock delays.
36
38
  * `animate_lock_graph` - creates animated locks graphs based on the `pg_lock_tracer` output.
37
39
 
38
- __Note:__ These tools employ the [eBPF](https://ebpf.io/) (_Extended Berkeley Packet Filter_) technology. At the moment, PostgreSQL 12, 13, 14, 15, and 16 are supported (see additional information below).
40
+ __Note:__ These tools employ the [eBPF](https://ebpf.io/) (_Extended Berkeley Packet Filter_) technology. At the moment, PostgreSQL 14, 15, 16, 17, and 18 are supported (see additional information below).
39
41
 
40
42
  # pg_lock_tracer
41
43
  `pg_lock_tracer` observes the locking activity of a running PostgreSQL process (using _eBPF_ and _UProbes_). In contrast to the information that is present in the table `pg_locks` (which provides information about which locks are _currently_ requested), `pg_lock_tracer` gives you a continuous view of the locking activity and collects statistics and timings.
@@ -44,6 +46,9 @@ The tracer also allows dumping the output as JSON formatted lines, which allows
44
46
 
45
47
  ## Usage Examples
46
48
  ```
49
+ # Trace use binary '/home/jan/postgresql-sandbox/bin/REL_15_1_DEBUG/bin/postgres' for tracing
50
+ pg_lock_tracer -x /home/jan/postgresql-sandbox/bin/REL_15_1_DEBUG/bin/postgres
51
+
47
52
  # Trace use binary '/home/jan/postgresql-sandbox/bin/REL_15_1_DEBUG/bin/postgres' for tracing and trace pid 1234
48
53
  pg_lock_tracer -x /home/jan/postgresql-sandbox/bin/REL_15_1_DEBUG/bin/postgres -p 1234
49
54
 
@@ -877,6 +882,53 @@ Lock results:
877
882
  +---------+-------+--------------+-----------------+------------+------------+------------------+---------------+
878
883
  ```
879
884
 
885
+ # pg_spinlock_delay_tracer
886
+ `pg_spinlock_delay_tracer` allows tracing spinlock delays in a PostgreSQL process. Spin locks are used in PostgreSQL to protect short critical sections in the code. If another process already holds a spinlock, the requesting process will repeatedly check (i.e., "spin") until the lock becomes available. If the lock is held for a longer period, PostgreSQL performs a ["spin delay"](https://github.com/postgres/postgres/blob/0c8e082fba8d36434552d3d7800abda54acafd57/src/backend/storage/lmgr/s_lock.c#L106) and yields the CPU for a short time to avoid busy-waiting. Such delays are reported via the `pg_spinlock_delay_tracer`. For each delay, it prints the content of the `SpinDelayStatus` structure, which contains information about the number of spins, delays, and the current delay time. Additionally, the function name and source code location where the spin delay occurred are reported.
887
+
888
+ ## Usage Examples
889
+ ```
890
+ # Trace the spinlock delays of the given PostgreSQL binary
891
+ pg_spinlock_delay_tracer -x /home/jan/postgresql-sandbox/bin/REL_17_1_DEBUG/bin/postgres
892
+
893
+ # Trace the spinlock delays of the PID 1234
894
+ pg_spinlock_delay_tracer -p 1234 -x /home/jan/postgresql-sandbox/bin/REL_17_1_DEBUG/bin/postgres
895
+ ```
896
+
897
+ ## Example output
898
+ To reproduce a spinlock delay, follow these steps to simulate a delay at the WAL insert position. First, start two different sessions connected to the same database. Then, create two tables:
899
+
900
+ ```sql
901
+ CREATE TABLE mydata1 (id INT);
902
+ CREATE TABLE mydata2 (id INT);
903
+ ```
904
+
905
+ Next, open a debugger and set a breakpoint in the function `ReserveXLogInsertLocation` after the spin lock `SpinLockAcquire(&Insert->insertpos_lck);` is acquired. Afterward, start the `pg_spinlock_delay_tracer` and perform two inserts in the two different sessions:
906
+
907
+ ```
908
+ # Session 1
909
+ INSERT INTO mydata1 VALUES(1);
910
+ # Session 2
911
+ INSERT INTO mydata2 VALUES(2);
912
+ ```
913
+
914
+ Note: Two different tables are used to prevent both sessions from trying to lock the same buffer and waiting for each other on a different lock.
915
+
916
+ The debugger should stop in the first session at the breakpoint. Furthermore, the pg_spinlock_delay_tracer should report spinlock delays in the second session, as the first session holds the spinlock for a longer period.
917
+
918
+ ```
919
+ pg_spinlock_delay_tracer -x /home/jan/postgresql-sandbox/bin/REL_17_1_DEBUG/bin/postgres
920
+ [...]
921
+ 13180680737869452 [Pid 1864403] SpinDelay spins=996 delays=939 cur_delay=566086 at ReserveXLogInsertLocation, xlog.c:1132
922
+ 13180680737874986 [Pid 1864403] SpinDelay spins=997 delays=939 cur_delay=566086 at ReserveXLogInsertLocation, xlog.c:1132
923
+ 13180680737880522 [Pid 1864403] SpinDelay spins=998 delays=939 cur_delay=566086 at ReserveXLogInsertLocation, xlog.c:1132
924
+ 13180680737886009 [Pid 1864403] SpinDelay spins=999 delays=939 cur_delay=566086 at ReserveXLogInsertLocation, xlog.c:1132
925
+ 13180681304189362 [Pid 1864403] SpinDelay spins=0 delays=940 cur_delay=661655 at ReserveXLogInsertLocation, xlog.c:1132
926
+ 13180681304227806 [Pid 1864403] SpinDelay spins=1 delays=940 cur_delay=661655 at ReserveXLogInsertLocation, xlog.c:1132
927
+ 13180681304241759 [Pid 1864403] SpinDelay spins=2 delays=940 cur_delay=661655 at ReserveXLogInsertLocation, xlog.c:1132
928
+ 13180681304255150 [Pid 1864403] SpinDelay spins=3 delays=940 cur_delay=661655 at ReserveXLogInsertLocation, xlog.c:1132
929
+ [...]
930
+ ```
931
+
880
932
  # Additional Information
881
933
 
882
934
  ## Installation
@@ -914,8 +966,8 @@ pip install git+https://github.com/jnidzwetzki/pg-lock-tracer
914
966
  ```
915
967
 
916
968
  ## PostgreSQL Build
917
- The software is tested with PostgreSQL versions 12, 13, 14, and 15. In order to be able to attach the _uprobes_ to the functions, they should not to be optimized away (e.g., inlined) during the compilation of PostgreSQL. Otherwise errors like `Unable to locate function XXX` will occur when `pg_lock_tracer` is started.
969
+ The software is tested with PostgreSQL versions 14, 15, 16, 17, and 18. In order to be able to attach the _uprobes_ to the functions, they should not to be optimized away (e.g., inlined) during the compilation of PostgreSQL. Otherwise errors like `Unable to locate function XXX` will occur when `pg_lock_tracer` is started.
918
970
 
919
- It is recommended to compile PostgreSQL with following CFLAGS: `CFLAGS="-ggdb -Og -g3 -fno-omit-frame-pointer"`.
971
+ It is recommended to compile PostgreSQL with the following CFLAGS: `CFLAGS="-ggdb -Og -g3 -fno-omit-frame-pointer"`.
920
972
 
921
973
  `pg_lw_lock_trace` uses [USDT probes](https://www.postgresql.org/docs/current/dynamic-trace.html). Therefore, PostgreSQL has to be compiled with `--enable-dtrace` to use this script.
@@ -1,25 +1,3 @@
1
- Metadata-Version: 2.1
2
- Name: pg_lock_tracer
3
- Version: 0.6.1
4
- Summary: A BPF based lock tracer for the PostgreSQL database
5
- Home-page: https://github.com/jnidzwetzki/pg-lock-tracer
6
- Author: Jan Nidzwetzki
7
- Project-URL: Bug Tracker, https://github.com/jnidzwetzki/pg-lock-tracer/issues
8
- Keywords: postgresql bpf lock locktracer
9
- Classifier: Development Status :: 4 - Beta
10
- Classifier: Intended Audience :: Developers
11
- Classifier: Operating System :: POSIX :: Linux
12
- Classifier: Programming Language :: Python
13
- Classifier: Topic :: Software Development :: Debuggers
14
- Classifier: License :: OSI Approved :: Apache Software License
15
- Requires-Python: >=3.6
16
- Description-Content-Type: text/markdown
17
- License-File: LICENSE
18
- Requires-Dist: graphviz
19
- Requires-Dist: igraph
20
- Requires-Dist: prettytable
21
- Requires-Dist: psycopg2
22
-
23
1
  # Lock tracing tools for PostgreSQL
24
2
  [![Make a PR](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com)
25
3
  [![Build Status](https://github.com/jnidzwetzki/pg-lock-tracer/actions/workflows/tests.yml/badge.svg)](https://github.com/jnidzwetzki/pg-lock-tracer/actions/workflows/tests.yml)
@@ -33,9 +11,10 @@ This project provides tools that allow you to gain deep insights into PostgreSQL
33
11
  * `pg_lock_tracer` - is a PostgreSQL table level lock tracer.
34
12
  * `pg_lw_lock_tracer` - is a tracer for PostgreSQL lightweight locks (LWLocks).
35
13
  * `pg_row_lock_tracer` - is a tracer for PostgreSQL row locks.
14
+ * `pg_spinlock_delay_tracer` - is a tracer for PostgreSQL spinlock delays.
36
15
  * `animate_lock_graph` - creates animated locks graphs based on the `pg_lock_tracer` output.
37
16
 
38
- __Note:__ These tools employ the [eBPF](https://ebpf.io/) (_Extended Berkeley Packet Filter_) technology. At the moment, PostgreSQL 12, 13, 14, 15, and 16 are supported (see additional information below).
17
+ __Note:__ These tools employ the [eBPF](https://ebpf.io/) (_Extended Berkeley Packet Filter_) technology. At the moment, PostgreSQL 14, 15, 16, 17, and 18 are supported (see additional information below).
39
18
 
40
19
  # pg_lock_tracer
41
20
  `pg_lock_tracer` observes the locking activity of a running PostgreSQL process (using _eBPF_ and _UProbes_). In contrast to the information that is present in the table `pg_locks` (which provides information about which locks are _currently_ requested), `pg_lock_tracer` gives you a continuous view of the locking activity and collects statistics and timings.
@@ -44,6 +23,9 @@ The tracer also allows dumping the output as JSON formatted lines, which allows
44
23
 
45
24
  ## Usage Examples
46
25
  ```
26
+ # Trace use binary '/home/jan/postgresql-sandbox/bin/REL_15_1_DEBUG/bin/postgres' for tracing
27
+ pg_lock_tracer -x /home/jan/postgresql-sandbox/bin/REL_15_1_DEBUG/bin/postgres
28
+
47
29
  # Trace use binary '/home/jan/postgresql-sandbox/bin/REL_15_1_DEBUG/bin/postgres' for tracing and trace pid 1234
48
30
  pg_lock_tracer -x /home/jan/postgresql-sandbox/bin/REL_15_1_DEBUG/bin/postgres -p 1234
49
31
 
@@ -877,6 +859,53 @@ Lock results:
877
859
  +---------+-------+--------------+-----------------+------------+------------+------------------+---------------+
878
860
  ```
879
861
 
862
+ # pg_spinlock_delay_tracer
863
+ `pg_spinlock_delay_tracer` allows tracing spinlock delays in a PostgreSQL process. Spin locks are used in PostgreSQL to protect short critical sections in the code. If another process already holds a spinlock, the requesting process will repeatedly check (i.e., "spin") until the lock becomes available. If the lock is held for a longer period, PostgreSQL performs a ["spin delay"](https://github.com/postgres/postgres/blob/0c8e082fba8d36434552d3d7800abda54acafd57/src/backend/storage/lmgr/s_lock.c#L106) and yields the CPU for a short time to avoid busy-waiting. Such delays are reported via the `pg_spinlock_delay_tracer`. For each delay, it prints the content of the `SpinDelayStatus` structure, which contains information about the number of spins, delays, and the current delay time. Additionally, the function name and source code location where the spin delay occurred are reported.
864
+
865
+ ## Usage Examples
866
+ ```
867
+ # Trace the spinlock delays of the given PostgreSQL binary
868
+ pg_spinlock_delay_tracer -x /home/jan/postgresql-sandbox/bin/REL_17_1_DEBUG/bin/postgres
869
+
870
+ # Trace the spinlock delays of the PID 1234
871
+ pg_spinlock_delay_tracer -p 1234 -x /home/jan/postgresql-sandbox/bin/REL_17_1_DEBUG/bin/postgres
872
+ ```
873
+
874
+ ## Example output
875
+ To reproduce a spinlock delay, follow these steps to simulate a delay at the WAL insert position. First, start two different sessions connected to the same database. Then, create two tables:
876
+
877
+ ```sql
878
+ CREATE TABLE mydata1 (id INT);
879
+ CREATE TABLE mydata2 (id INT);
880
+ ```
881
+
882
+ Next, open a debugger and set a breakpoint in the function `ReserveXLogInsertLocation` after the spin lock `SpinLockAcquire(&Insert->insertpos_lck);` is acquired. Afterward, start the `pg_spinlock_delay_tracer` and perform two inserts in the two different sessions:
883
+
884
+ ```
885
+ # Session 1
886
+ INSERT INTO mydata1 VALUES(1);
887
+ # Session 2
888
+ INSERT INTO mydata2 VALUES(2);
889
+ ```
890
+
891
+ Note: Two different tables are used to prevent both sessions from trying to lock the same buffer and waiting for each other on a different lock.
892
+
893
+ The debugger should stop in the first session at the breakpoint. Furthermore, the pg_spinlock_delay_tracer should report spinlock delays in the second session, as the first session holds the spinlock for a longer period.
894
+
895
+ ```
896
+ pg_spinlock_delay_tracer -x /home/jan/postgresql-sandbox/bin/REL_17_1_DEBUG/bin/postgres
897
+ [...]
898
+ 13180680737869452 [Pid 1864403] SpinDelay spins=996 delays=939 cur_delay=566086 at ReserveXLogInsertLocation, xlog.c:1132
899
+ 13180680737874986 [Pid 1864403] SpinDelay spins=997 delays=939 cur_delay=566086 at ReserveXLogInsertLocation, xlog.c:1132
900
+ 13180680737880522 [Pid 1864403] SpinDelay spins=998 delays=939 cur_delay=566086 at ReserveXLogInsertLocation, xlog.c:1132
901
+ 13180680737886009 [Pid 1864403] SpinDelay spins=999 delays=939 cur_delay=566086 at ReserveXLogInsertLocation, xlog.c:1132
902
+ 13180681304189362 [Pid 1864403] SpinDelay spins=0 delays=940 cur_delay=661655 at ReserveXLogInsertLocation, xlog.c:1132
903
+ 13180681304227806 [Pid 1864403] SpinDelay spins=1 delays=940 cur_delay=661655 at ReserveXLogInsertLocation, xlog.c:1132
904
+ 13180681304241759 [Pid 1864403] SpinDelay spins=2 delays=940 cur_delay=661655 at ReserveXLogInsertLocation, xlog.c:1132
905
+ 13180681304255150 [Pid 1864403] SpinDelay spins=3 delays=940 cur_delay=661655 at ReserveXLogInsertLocation, xlog.c:1132
906
+ [...]
907
+ ```
908
+
880
909
  # Additional Information
881
910
 
882
911
  ## Installation
@@ -914,8 +943,8 @@ pip install git+https://github.com/jnidzwetzki/pg-lock-tracer
914
943
  ```
915
944
 
916
945
  ## PostgreSQL Build
917
- The software is tested with PostgreSQL versions 12, 13, 14, and 15. In order to be able to attach the _uprobes_ to the functions, they should not to be optimized away (e.g., inlined) during the compilation of PostgreSQL. Otherwise errors like `Unable to locate function XXX` will occur when `pg_lock_tracer` is started.
946
+ The software is tested with PostgreSQL versions 14, 15, 16, 17, and 18. In order to be able to attach the _uprobes_ to the functions, they should not to be optimized away (e.g., inlined) during the compilation of PostgreSQL. Otherwise errors like `Unable to locate function XXX` will occur when `pg_lock_tracer` is started.
918
947
 
919
- It is recommended to compile PostgreSQL with following CFLAGS: `CFLAGS="-ggdb -Og -g3 -fno-omit-frame-pointer"`.
948
+ It is recommended to compile PostgreSQL with the following CFLAGS: `CFLAGS="-ggdb -Og -g3 -fno-omit-frame-pointer"`.
920
949
 
921
950
  `pg_lw_lock_trace` uses [USDT probes](https://www.postgresql.org/docs/current/dynamic-trace.html). Therefore, PostgreSQL has to be compiled with `--enable-dtrace` to use this script.
@@ -0,0 +1,62 @@
1
+ [build-system]
2
+ requires = ["setuptools>=59"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pg_lock_tracer"
7
+ description = "An eBPF based lock tracer for PostgreSQL"
8
+ readme = { file = "README.md", content-type = "text/markdown" }
9
+ requires-python = ">=3.10"
10
+ license = "Apache-2.0"
11
+ license-files = [
12
+ "LICENSE",
13
+ ]
14
+ authors = [
15
+ { name = "Jan Nidzwetzki", email = "jnidzwetzki@gmx.de" },
16
+ ]
17
+ keywords = ["postgresql", "postgres", "ebpf", "locktracer"]
18
+ classifiers = [
19
+ "Development Status :: 4 - Beta",
20
+ "Intended Audience :: Developers",
21
+ "Operating System :: POSIX :: Linux",
22
+ "Programming Language :: Python",
23
+ "Topic :: Software Development :: Debuggers"
24
+ ]
25
+ dependencies = [
26
+ "graphviz",
27
+ "igraph",
28
+ "prettytable",
29
+ "psycopg2",
30
+ ]
31
+ dynamic = ["version"]
32
+
33
+ [project.urls]
34
+ Homepage = "https://github.com/jnidzwetzki/pg-lock-tracer"
35
+ "Bug Tracker" = "https://github.com/jnidzwetzki/pg-lock-tracer/issues"
36
+
37
+ [project.scripts]
38
+ pg_lock_tracer = "pg_lock_tracer.pg_lock_tracer:main"
39
+ pg_lw_lock_tracer = "pg_lock_tracer.pg_lw_lock_tracer:main"
40
+ pg_row_lock_tracer = "pg_lock_tracer.pg_row_lock_tracer:main"
41
+ pg_spinlock_delay_tracer = "pg_lock_tracer.pg_spinlock_delay_tracer:main"
42
+ animate_lock_graph = "pg_lock_tracer.animate_lock_graph:main"
43
+
44
+ [tool.setuptools]
45
+ package-dir = { "" = "src" }
46
+
47
+ [tool.setuptools.packages.find]
48
+ where = ["src"]
49
+
50
+ [tool.setuptools.package-data]
51
+ "pg_lock_tracer.bpf" = ["*.c"]
52
+
53
+ [tool.setuptools.dynamic]
54
+ version = { attr = "pg_lock_tracer.__version__" }
55
+
56
+ [tool.pytest.ini_options]
57
+ testpaths = ["tests/"]
58
+ addopts = [
59
+ "--verbose",
60
+ "--ignore-glob=**/_*.py",
61
+ ]
62
+
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ __version__ = "0.7.1"
@@ -0,0 +1,57 @@
1
+ #include <uapi/linux/ptrace.h>
2
+
3
+ #define MAX_STR_LEN 128
4
+
5
+ typedef struct SpinDelayStatus_t {
6
+ int spins;
7
+ int delays;
8
+ int cur_delay;
9
+ const char *file;
10
+ int line;
11
+ const char *func;
12
+ } SpinDelayStatus;
13
+
14
+ typedef struct SpinDelayEvent_t {
15
+ u32 pid;
16
+ u64 timestamp;
17
+ int spins;
18
+ int delays;
19
+ int cur_delay;
20
+ int line;
21
+ char file[MAX_STR_LEN];
22
+ char func[MAX_STR_LEN];
23
+ } SpinDelayEvent;
24
+
25
+ BPF_PERF_OUTPUT(lockevents);
26
+
27
+ int spin_delay(struct pt_regs *ctx) {
28
+ SpinDelayEvent event = {};
29
+ SpinDelayStatus status = {};
30
+ SpinDelayStatus *status_ptr = (SpinDelayStatus *)PT_REGS_PARM1(ctx);
31
+
32
+ event.pid = bpf_get_current_pid_tgid();
33
+ event.timestamp = bpf_ktime_get_ns();
34
+
35
+ if (!status_ptr) {
36
+ lockevents.perf_submit(ctx, &event, sizeof(SpinDelayEvent));
37
+ return 0;
38
+ }
39
+
40
+ bpf_probe_read_user(&status, sizeof(status), status_ptr);
41
+
42
+ event.spins = status.spins;
43
+ event.delays = status.delays;
44
+ event.cur_delay = status.cur_delay;
45
+ event.line = status.line;
46
+
47
+ if (status.file) {
48
+ bpf_probe_read_user_str(&event.file, sizeof(event.file), status.file);
49
+ }
50
+
51
+ if (status.func) {
52
+ bpf_probe_read_user_str(&event.func, sizeof(event.func), status.func);
53
+ }
54
+
55
+ lockevents.perf_submit(ctx, &event, sizeof(SpinDelayEvent));
56
+ return 0;
57
+ }
@@ -34,7 +34,7 @@ class OIDResolver:
34
34
  host=hostname,
35
35
  port=port,
36
36
  )
37
-
37
+ self.connection.set_session(autocommit=True)
38
38
  self.cur = self.connection.cursor()
39
39
 
40
40
  # Warmup cache
@@ -470,7 +470,8 @@ class PGLockTraceOutputHuman(PGLockTraceOutput):
470
470
  )
471
471
  line = line.decode("utf-8")
472
472
  # Get line with: 'gdb info line *(symbol+0x1111)'
473
- print(f"\t{line}")
473
+ line = f"\t{line}"
474
+ self.handle_output_line(line)
474
475
 
475
476
 
476
477
  class PGLockTraceOutputJSON(PGLockTraceOutput):
@@ -480,7 +481,11 @@ class PGLockTraceOutputJSON(PGLockTraceOutput):
480
481
  """
481
482
  event = self.bpf_instance["lockevents"].event(data)
482
483
 
483
- if event.pid not in self.pids and event.event_type < Events.GLOBAL:
484
+ if (
485
+ self.pids
486
+ and event.pid not in self.pids
487
+ and event.event_type < Events.GLOBAL
488
+ ):
484
489
  return
485
490
 
486
491
  output = {}
@@ -0,0 +1,159 @@
1
+ #!/usr/bin/env python3
2
+ #
3
+ # PostgreSQL spinlock delay tracer.
4
+ #
5
+ ###############################################
6
+
7
+ import sys
8
+ import argparse
9
+
10
+ from bcc import BPF
11
+
12
+ from pg_lock_tracer import __version__
13
+ from pg_lock_tracer.helper import BPFHelper
14
+
15
+ EXAMPLES = """examples:
16
+ # Trace spin delays of the given PostgreSQL binary
17
+ pg_spinlock_delay_tracer -x /home/jan/postgresql-sandbox/bin/REL_14_9_DEBUG/bin/postgres
18
+
19
+ # Trace spin delays of the PID 1234
20
+ pg_spinlock_delay_tracer -p 1234 -x /home/jan/postgresql-sandbox/bin/REL_14_9_DEBUG/bin/postgres
21
+
22
+ # Trace spin delays of the PID 1234 and 5678
23
+ pg_spinlock_delay_tracer -p 1234 -p 5678 -x /home/jan/postgresql-sandbox/bin/REL_14_9_DEBUG/bin/postgres
24
+
25
+ # Trace spin delays of the PID 1234 and be verbose
26
+ pg_spinlock_delay_tracer -p 1234 -x /home/jan/postgresql-sandbox/bin/REL_14_9_DEBUG/bin/postgres -v
27
+ """
28
+
29
+ parser = argparse.ArgumentParser(
30
+ description="",
31
+ formatter_class=argparse.RawDescriptionHelpFormatter,
32
+ epilog=EXAMPLES,
33
+ )
34
+ parser.add_argument(
35
+ "-V",
36
+ "--version",
37
+ action="version",
38
+ version=f"{parser.prog} ({__version__})",
39
+ )
40
+ parser.add_argument("-v", "--verbose", action="store_true", help="Be verbose")
41
+ parser.add_argument(
42
+ "-p",
43
+ "--pid",
44
+ type=int,
45
+ nargs="+",
46
+ dest="pids",
47
+ metavar="PID",
48
+ help="the pid(s) to trace",
49
+ )
50
+ parser.add_argument(
51
+ "-x",
52
+ "--exe",
53
+ type=str,
54
+ required=True,
55
+ dest="path",
56
+ metavar="PATH",
57
+ help="path to binary",
58
+ )
59
+ parser.add_argument(
60
+ "-d",
61
+ "--dry-run",
62
+ action="store_true",
63
+ help="compile and load the BPF program but exit afterward",
64
+ )
65
+
66
+
67
+ class PGSpinDelayTracer:
68
+ def __init__(self, prog_args):
69
+ self.bpf_instance = None
70
+ self.args = prog_args
71
+
72
+ # Belong the processes to the binary?
73
+ BPFHelper.check_pid_exe(self.args.pids, self.args.path)
74
+
75
+ @staticmethod
76
+ def _decode_field(value):
77
+ return value.decode("utf-8", "replace").rstrip("\x00")
78
+
79
+ def print_lock_event(self, _cpu, data, _size):
80
+ """
81
+ Print a new spin delay event.
82
+ """
83
+ event = self.bpf_instance["lockevents"].event(data)
84
+
85
+ if self.args.pids and event.pid not in self.args.pids:
86
+ return
87
+
88
+ file_name = self._decode_field(event.file) or "(unknown)"
89
+ func_name = self._decode_field(event.func) or "(unknown)"
90
+
91
+ print(
92
+ f"{event.timestamp} [Pid {event.pid}] SpinDelay "
93
+ f"spins={event.spins} delays={event.delays} "
94
+ f"cur_delay={event.cur_delay} at {func_name}, {file_name}:{event.line}"
95
+ )
96
+
97
+ def init(self):
98
+ """
99
+ Init the PostgreSQL spin delay tracer
100
+ """
101
+ bpf_program = BPFHelper.read_bpf_program("pg_spinlock_delay_tracer.c")
102
+
103
+ if self.args.verbose:
104
+ print(bpf_program)
105
+
106
+ # Disable warnings like
107
+ # 'warning: '__HAVE_BUILTIN_BSWAP32__' macro redefined [-Wmacro-redefined]'
108
+ bpf_cflags = ["-Wno-macro-redefined"] if not self.args.verbose else []
109
+
110
+ print("===> Compiling BPF program")
111
+ self.bpf_instance = BPF(text=bpf_program, cflags=bpf_cflags)
112
+
113
+ print("===> Attaching BPF probes")
114
+ self.attach_probes()
115
+
116
+ # Open the event queue
117
+ self.bpf_instance["lockevents"].open_perf_buffer(
118
+ self.print_lock_event, page_cnt=BPFHelper.page_cnt
119
+ )
120
+
121
+ def attach_probes(self):
122
+ """
123
+ Attach BPF probes
124
+ """
125
+ BPFHelper.register_ebpf_probe(
126
+ self.args.path,
127
+ self.bpf_instance,
128
+ "^perform_spin_delay$",
129
+ "spin_delay",
130
+ self.args.verbose,
131
+ )
132
+
133
+ def run(self):
134
+ """
135
+ Run the BPF program and read results
136
+ """
137
+ print("===> Ready to trace")
138
+ while True:
139
+ try:
140
+ self.bpf_instance.perf_buffer_poll()
141
+ except KeyboardInterrupt:
142
+ sys.exit(0)
143
+
144
+
145
+ def main():
146
+ """
147
+ Entry point for the BPF based PostgreSQL spin delay tracer.
148
+ """
149
+ args = parser.parse_args()
150
+
151
+ pg_spin_delay_tracer = PGSpinDelayTracer(args)
152
+ pg_spin_delay_tracer.init()
153
+
154
+ if not args.dry_run:
155
+ pg_spin_delay_tracer.run()
156
+
157
+
158
+ if __name__ == "__main__":
159
+ main()
@@ -1,3 +1,26 @@
1
+ Metadata-Version: 2.4
2
+ Name: pg_lock_tracer
3
+ Version: 0.7.1
4
+ Summary: An eBPF based lock tracer for PostgreSQL
5
+ Author-email: Jan Nidzwetzki <jnidzwetzki@gmx.de>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/jnidzwetzki/pg-lock-tracer
8
+ Project-URL: Bug Tracker, https://github.com/jnidzwetzki/pg-lock-tracer/issues
9
+ Keywords: postgresql,postgres,ebpf,locktracer
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: POSIX :: Linux
13
+ Classifier: Programming Language :: Python
14
+ Classifier: Topic :: Software Development :: Debuggers
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: graphviz
19
+ Requires-Dist: igraph
20
+ Requires-Dist: prettytable
21
+ Requires-Dist: psycopg2
22
+ Dynamic: license-file
23
+
1
24
  # Lock tracing tools for PostgreSQL
2
25
  [![Make a PR](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com)
3
26
  [![Build Status](https://github.com/jnidzwetzki/pg-lock-tracer/actions/workflows/tests.yml/badge.svg)](https://github.com/jnidzwetzki/pg-lock-tracer/actions/workflows/tests.yml)
@@ -11,9 +34,10 @@ This project provides tools that allow you to gain deep insights into PostgreSQL
11
34
  * `pg_lock_tracer` - is a PostgreSQL table level lock tracer.
12
35
  * `pg_lw_lock_tracer` - is a tracer for PostgreSQL lightweight locks (LWLocks).
13
36
  * `pg_row_lock_tracer` - is a tracer for PostgreSQL row locks.
37
+ * `pg_spinlock_delay_tracer` - is a tracer for PostgreSQL spinlock delays.
14
38
  * `animate_lock_graph` - creates animated locks graphs based on the `pg_lock_tracer` output.
15
39
 
16
- __Note:__ These tools employ the [eBPF](https://ebpf.io/) (_Extended Berkeley Packet Filter_) technology. At the moment, PostgreSQL 12, 13, 14, 15, and 16 are supported (see additional information below).
40
+ __Note:__ These tools employ the [eBPF](https://ebpf.io/) (_Extended Berkeley Packet Filter_) technology. At the moment, PostgreSQL 14, 15, 16, 17, and 18 are supported (see additional information below).
17
41
 
18
42
  # pg_lock_tracer
19
43
  `pg_lock_tracer` observes the locking activity of a running PostgreSQL process (using _eBPF_ and _UProbes_). In contrast to the information that is present in the table `pg_locks` (which provides information about which locks are _currently_ requested), `pg_lock_tracer` gives you a continuous view of the locking activity and collects statistics and timings.
@@ -22,6 +46,9 @@ The tracer also allows dumping the output as JSON formatted lines, which allows
22
46
 
23
47
  ## Usage Examples
24
48
  ```
49
+ # Trace use binary '/home/jan/postgresql-sandbox/bin/REL_15_1_DEBUG/bin/postgres' for tracing
50
+ pg_lock_tracer -x /home/jan/postgresql-sandbox/bin/REL_15_1_DEBUG/bin/postgres
51
+
25
52
  # Trace use binary '/home/jan/postgresql-sandbox/bin/REL_15_1_DEBUG/bin/postgres' for tracing and trace pid 1234
26
53
  pg_lock_tracer -x /home/jan/postgresql-sandbox/bin/REL_15_1_DEBUG/bin/postgres -p 1234
27
54
 
@@ -855,6 +882,53 @@ Lock results:
855
882
  +---------+-------+--------------+-----------------+------------+------------+------------------+---------------+
856
883
  ```
857
884
 
885
+ # pg_spinlock_delay_tracer
886
+ `pg_spinlock_delay_tracer` allows tracing spinlock delays in a PostgreSQL process. Spin locks are used in PostgreSQL to protect short critical sections in the code. If another process already holds a spinlock, the requesting process will repeatedly check (i.e., "spin") until the lock becomes available. If the lock is held for a longer period, PostgreSQL performs a ["spin delay"](https://github.com/postgres/postgres/blob/0c8e082fba8d36434552d3d7800abda54acafd57/src/backend/storage/lmgr/s_lock.c#L106) and yields the CPU for a short time to avoid busy-waiting. Such delays are reported via the `pg_spinlock_delay_tracer`. For each delay, it prints the content of the `SpinDelayStatus` structure, which contains information about the number of spins, delays, and the current delay time. Additionally, the function name and source code location where the spin delay occurred are reported.
887
+
888
+ ## Usage Examples
889
+ ```
890
+ # Trace the spinlock delays of the given PostgreSQL binary
891
+ pg_spinlock_delay_tracer -x /home/jan/postgresql-sandbox/bin/REL_17_1_DEBUG/bin/postgres
892
+
893
+ # Trace the spinlock delays of the PID 1234
894
+ pg_spinlock_delay_tracer -p 1234 -x /home/jan/postgresql-sandbox/bin/REL_17_1_DEBUG/bin/postgres
895
+ ```
896
+
897
+ ## Example output
898
+ To reproduce a spinlock delay, follow these steps to simulate a delay at the WAL insert position. First, start two different sessions connected to the same database. Then, create two tables:
899
+
900
+ ```sql
901
+ CREATE TABLE mydata1 (id INT);
902
+ CREATE TABLE mydata2 (id INT);
903
+ ```
904
+
905
+ Next, open a debugger and set a breakpoint in the function `ReserveXLogInsertLocation` after the spin lock `SpinLockAcquire(&Insert->insertpos_lck);` is acquired. Afterward, start the `pg_spinlock_delay_tracer` and perform two inserts in the two different sessions:
906
+
907
+ ```
908
+ # Session 1
909
+ INSERT INTO mydata1 VALUES(1);
910
+ # Session 2
911
+ INSERT INTO mydata2 VALUES(2);
912
+ ```
913
+
914
+ Note: Two different tables are used to prevent both sessions from trying to lock the same buffer and waiting for each other on a different lock.
915
+
916
+ The debugger should stop in the first session at the breakpoint. Furthermore, the pg_spinlock_delay_tracer should report spinlock delays in the second session, as the first session holds the spinlock for a longer period.
917
+
918
+ ```
919
+ pg_spinlock_delay_tracer -x /home/jan/postgresql-sandbox/bin/REL_17_1_DEBUG/bin/postgres
920
+ [...]
921
+ 13180680737869452 [Pid 1864403] SpinDelay spins=996 delays=939 cur_delay=566086 at ReserveXLogInsertLocation, xlog.c:1132
922
+ 13180680737874986 [Pid 1864403] SpinDelay spins=997 delays=939 cur_delay=566086 at ReserveXLogInsertLocation, xlog.c:1132
923
+ 13180680737880522 [Pid 1864403] SpinDelay spins=998 delays=939 cur_delay=566086 at ReserveXLogInsertLocation, xlog.c:1132
924
+ 13180680737886009 [Pid 1864403] SpinDelay spins=999 delays=939 cur_delay=566086 at ReserveXLogInsertLocation, xlog.c:1132
925
+ 13180681304189362 [Pid 1864403] SpinDelay spins=0 delays=940 cur_delay=661655 at ReserveXLogInsertLocation, xlog.c:1132
926
+ 13180681304227806 [Pid 1864403] SpinDelay spins=1 delays=940 cur_delay=661655 at ReserveXLogInsertLocation, xlog.c:1132
927
+ 13180681304241759 [Pid 1864403] SpinDelay spins=2 delays=940 cur_delay=661655 at ReserveXLogInsertLocation, xlog.c:1132
928
+ 13180681304255150 [Pid 1864403] SpinDelay spins=3 delays=940 cur_delay=661655 at ReserveXLogInsertLocation, xlog.c:1132
929
+ [...]
930
+ ```
931
+
858
932
  # Additional Information
859
933
 
860
934
  ## Installation
@@ -892,8 +966,8 @@ pip install git+https://github.com/jnidzwetzki/pg-lock-tracer
892
966
  ```
893
967
 
894
968
  ## PostgreSQL Build
895
- The software is tested with PostgreSQL versions 12, 13, 14, and 15. In order to be able to attach the _uprobes_ to the functions, they should not to be optimized away (e.g., inlined) during the compilation of PostgreSQL. Otherwise errors like `Unable to locate function XXX` will occur when `pg_lock_tracer` is started.
969
+ The software is tested with PostgreSQL versions 14, 15, 16, 17, and 18. In order to be able to attach the _uprobes_ to the functions, they should not to be optimized away (e.g., inlined) during the compilation of PostgreSQL. Otherwise errors like `Unable to locate function XXX` will occur when `pg_lock_tracer` is started.
896
970
 
897
- It is recommended to compile PostgreSQL with following CFLAGS: `CFLAGS="-ggdb -Og -g3 -fno-omit-frame-pointer"`.
971
+ It is recommended to compile PostgreSQL with the following CFLAGS: `CFLAGS="-ggdb -Og -g3 -fno-omit-frame-pointer"`.
898
972
 
899
973
  `pg_lw_lock_trace` uses [USDT probes](https://www.postgresql.org/docs/current/dynamic-trace.html). Therefore, PostgreSQL has to be compiled with `--enable-dtrace` to use this script.
@@ -1,7 +1,6 @@
1
1
  LICENSE
2
2
  README.md
3
3
  pyproject.toml
4
- setup.cfg
5
4
  setup.py
6
5
  src/pg_lock_tracer/__init__.py
7
6
  src/pg_lock_tracer/animate_lock_graph.py
@@ -10,6 +9,7 @@ src/pg_lock_tracer/oid_resolver.py
10
9
  src/pg_lock_tracer/pg_lock_tracer.py
11
10
  src/pg_lock_tracer/pg_lw_lock_tracer.py
12
11
  src/pg_lock_tracer/pg_row_lock_tracer.py
12
+ src/pg_lock_tracer/pg_spinlock_delay_tracer.py
13
13
  src/pg_lock_tracer.egg-info/PKG-INFO
14
14
  src/pg_lock_tracer.egg-info/SOURCES.txt
15
15
  src/pg_lock_tracer.egg-info/dependency_links.txt
@@ -20,4 +20,5 @@ src/pg_lock_tracer/bpf/__init__.py
20
20
  src/pg_lock_tracer/bpf/pg_lock_tracer.c
21
21
  src/pg_lock_tracer/bpf/pg_lw_lock_tracer.c
22
22
  src/pg_lock_tracer/bpf/pg_row_lock_tracer.c
23
+ src/pg_lock_tracer/bpf/pg_spinlock_delay_tracer.c
23
24
  tests/test_helper.py
@@ -3,3 +3,4 @@ animate_lock_graph = pg_lock_tracer.animate_lock_graph:main
3
3
  pg_lock_tracer = pg_lock_tracer.pg_lock_tracer:main
4
4
  pg_lw_lock_tracer = pg_lock_tracer.pg_lw_lock_tracer:main
5
5
  pg_row_lock_tracer = pg_lock_tracer.pg_row_lock_tracer:main
6
+ pg_spinlock_delay_tracer = pg_lock_tracer.pg_spinlock_delay_tracer:main
@@ -1,3 +0,0 @@
1
- [build-system]
2
- requires = ["setuptools>=59"]
3
- build-backend = "setuptools.build_meta"
@@ -1,57 +0,0 @@
1
- [metadata]
2
- name = pg_lock_tracer
3
- version = attr: pg_lock_tracer.__version__
4
- description = A BPF based lock tracer for the PostgreSQL database
5
- long_description = file: README.md
6
- long_description_content_type = text/markdown
7
- author = Jan Nidzwetzki
8
- url = https://github.com/jnidzwetzki/pg-lock-tracer
9
- project_urls =
10
- Bug Tracker = https://github.com/jnidzwetzki/pg-lock-tracer/issues
11
- keywords = postgresql bpf lock locktracer
12
- classifiers =
13
- Development Status :: 4 - Beta
14
- Intended Audience :: Developers
15
- Operating System :: POSIX :: Linux
16
- Programming Language :: Python
17
- Topic :: Software Development :: Debuggers
18
- License :: OSI Approved :: Apache Software License
19
-
20
- [options]
21
- package_dir =
22
- = src
23
- packages = find:
24
- python_requires = >= 3.6
25
- install_requires =
26
- graphviz
27
- igraph
28
- prettytable
29
- psycopg2
30
-
31
- [aliases]
32
- test = pytest
33
-
34
- [tool:pytest]
35
- testpaths = tests/
36
- addopts =
37
- --verbose
38
- --ignore-glob='**/_*.py'
39
-
40
- [options.packages.find]
41
- where = src
42
-
43
- [options.package_data]
44
- pg_lock_tracer.bpf =
45
- *.c
46
-
47
- [options.entry_points]
48
- console_scripts =
49
- pg_lock_tracer = pg_lock_tracer.pg_lock_tracer:main
50
- pg_lw_lock_tracer = pg_lock_tracer.pg_lw_lock_tracer:main
51
- pg_row_lock_tracer = pg_lock_tracer.pg_row_lock_tracer:main
52
- animate_lock_graph = pg_lock_tracer.animate_lock_graph:main
53
-
54
- [egg_info]
55
- tag_build =
56
- tag_date = 0
57
-
@@ -1 +0,0 @@
1
- __version__ = "0.6.1"
File without changes
File without changes