vivarium 0.1.1 → 0.2.0
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.
- checksums.yaml +4 -4
- data/README.md +67 -7
- data/examples/execve_demo.rb +49 -0
- data/examples/file_operation_demo.rb +68 -0
- data/examples/network_client_demo.rb +76 -0
- data/examples/privilege_event_demo.rb +38 -0
- data/examples/signal_kill_demo.rb +38 -0
- data/lib/vivarium/logger.rb +14 -2
- data/lib/vivarium/version.rb +1 -1
- data/lib/vivarium.rb +1221 -136
- data/sig/vivarium.rbs +28 -0
- metadata +22 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 973df86f00b8e2ca9d5e963958562e2f58822e13c0e4d33af53e85a8d38f12be
|
|
4
|
+
data.tar.gz: a5651655ef69c10e6b6eab89baf9f98e5d99a158a8e89a6fc940fe2d844ab636
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3606b1f6df38a813e2b0cdf48aef48312be1a7c0bd3f20f88ca8a4c527c724733184c1c0d52f39fc9242282ab6d41ca18f6bcc977f4cafad0c5d4faa17a27d78
|
|
7
|
+
data.tar.gz: 4171eefdd7052d8634cd5a49267c8fe4f925e87f5b18691cb0721dd3f014efac68475196762e9ecc800627bcc529d9077e088b7d0366d91a6252867d3a0fc5f1
|
data/README.md
CHANGED
|
@@ -18,10 +18,24 @@ The goal is to visualize which Ruby method context triggered low-level events.
|
|
|
18
18
|
Implemented in this repository:
|
|
19
19
|
|
|
20
20
|
- BPF LSM hook on `file_open`
|
|
21
|
+
- BPF LSM hooks on `inode_symlink`, `inode_link`, `inode_rename`, `path_chmod`
|
|
22
|
+
- BPF tracepoint on `sys_enter_getdents64`
|
|
23
|
+
- BPF tracepoint on `sys_enter_execve` (captures executable path and first few argv entries as `proc_exec`)
|
|
24
|
+
- BPF LSM hooks for suspicious behavior checks:
|
|
25
|
+
- `ptrace_access_check` (emits `ptrace_check`)
|
|
26
|
+
- `sb_mount` (emits `sb_mount`)
|
|
27
|
+
- `kernel_read_file` (emits `kernel_read_file`)
|
|
28
|
+
- `task_kill` (emits `task_kill`)
|
|
29
|
+
- `task_fix_setuid` (emits `setid_change`)
|
|
30
|
+
- `capable` for high-risk capabilities only (emits `capable_check`)
|
|
31
|
+
- `bprm_creds_from_file` (emits `bprm_creds`)
|
|
32
|
+
- BPF LSM hook on `socket_create` (flags unusual socket creation as `odd_socket`)
|
|
33
|
+
- BPF LSM hook on `socket_connect` (captures destination family/address/port as `sock_connect`)
|
|
34
|
+
- BPF tracepoints on `sys_enter_sendmsg`, `sys_enter_sendto`, `sys_enter_sendmmsg` (capture UDP/53 DNS QNAME raw bytes as `dns_req`)
|
|
21
35
|
- Shared pinned maps on bpffs
|
|
22
36
|
- `config_root_targets` (root PID -> 0/1)
|
|
23
37
|
- `config_spawned_targets` (spawned TID -> 0/1)
|
|
24
|
-
- `event_invoked` (array length
|
|
38
|
+
- `event_invoked` (array length 1024 with `event_t` records)
|
|
25
39
|
- `event_write_pos` (cursor for appending into `event_invoked`)
|
|
26
40
|
- Ruby API `Vivarium.observe do ... end`
|
|
27
41
|
- Registers current PID to `config_root_targets`
|
|
@@ -35,9 +49,10 @@ Implemented in this repository:
|
|
|
35
49
|
|
|
36
50
|
```c
|
|
37
51
|
struct event_t {
|
|
52
|
+
u64 ktime_ns;
|
|
38
53
|
u32 pid;
|
|
39
|
-
char event_name[
|
|
40
|
-
char payload[
|
|
54
|
+
char event_name[16];
|
|
55
|
+
char payload[256];
|
|
41
56
|
};
|
|
42
57
|
```
|
|
43
58
|
|
|
@@ -45,7 +60,7 @@ struct event_t {
|
|
|
45
60
|
|
|
46
61
|
- Linux kernel/environment supporting BPF LSM
|
|
47
62
|
- `libbcc` installed
|
|
48
|
-
- `bpftool` installed (used to resolve `struct file::f_path`
|
|
63
|
+
- `bpftool` installed (used to resolve `struct file::f_path` and `struct dentry::d_name` offsets from BTF)
|
|
49
64
|
- root privileges for `vivariumd`
|
|
50
65
|
- bpffs mounted (typically `/sys/fs/bpf`)
|
|
51
66
|
|
|
@@ -81,6 +96,46 @@ Vivarium.observe do
|
|
|
81
96
|
end
|
|
82
97
|
```
|
|
83
98
|
|
|
99
|
+
3) Network monitoring demo client:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
bundle exec ruby examples/network_client_demo.rb
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
This demo intentionally triggers `sock_connect`, `dns_req`, and `odd_socket` events.
|
|
106
|
+
|
|
107
|
+
4) File operation demo client (only touches `/tmp`):
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
bundle exec ruby examples/file_operation_demo.rb
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
This demo intentionally triggers `path_open`, `file_symlink`, `file_hardlink`, `file_rename`, `file_chmod`, and `file_getdents` events under `/tmp`.
|
|
114
|
+
|
|
115
|
+
5) Execve demo client:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
bundle exec ruby examples/execve_demo.rb
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
This demo intentionally triggers `proc_exec` with several argument patterns using direct `execve`-style process launches.
|
|
122
|
+
|
|
123
|
+
6) Signal demo client:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
bundle exec ruby examples/signal_kill_demo.rb
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
This demo forks a child process and sends `TERM` with `Process.kill`, which is useful for triggering `task_kill`.
|
|
130
|
+
|
|
131
|
+
7) Privilege-related event demo client:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
bundle exec ruby examples/privilege_event_demo.rb
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
This demo attempts setuid/setgid changes, sensitive file access, and `sudo` exec to trigger privilege-related events such as `setid_change`, `capable_check`, and `bprm_creds`.
|
|
138
|
+
|
|
84
139
|
You can also start top-level observation without a block (it keeps observing until process exit):
|
|
85
140
|
|
|
86
141
|
```ruby
|
|
@@ -124,11 +179,16 @@ bundle exec vivariumd --pin-dir /sys/fs/bpf/vivarium
|
|
|
124
179
|
## Notes
|
|
125
180
|
|
|
126
181
|
- Thread/Ractor-awareness is not yet implemented.
|
|
127
|
-
- `event_invoked` uses fixed
|
|
128
|
-
-
|
|
182
|
+
- `event_invoked` uses fixed 1024 slots and wraps around when full.
|
|
183
|
+
- `payload` is 256 bytes in `event_t`; some event types intentionally use smaller structured slices inside that buffer.
|
|
184
|
+
- `proc_exec` currently stores the executable path plus up to 3 argv entries in 4 fixed 64-byte slots to keep the BPF verifier happy.
|
|
185
|
+
- Each event is tagged with severity metadata: `high` for `setid_change`, `capable_check`, `bprm_creds`, `task_kill`, `ptrace_check`, `sb_mount`, and `kernel_read_file`; others are `medium` by default.
|
|
186
|
+
- In `human` format output, `high` severity events are rendered in red.
|
|
187
|
+
- `capable_check` is intentionally filtered to high-risk capabilities to reduce noise from extremely frequent `capable` hook calls.
|
|
129
188
|
- Current output format is textual and intended for iteration.
|
|
130
189
|
- `vivariumd` resolves `struct file::f_path` offset from `/sys/kernel/btf/vmlinux` at startup.
|
|
131
|
-
-
|
|
190
|
+
- `vivariumd` also resolves `struct dentry::d_name` offset from `/sys/kernel/btf/vmlinux` at startup.
|
|
191
|
+
- You can override offsets manually with `VIVARIUM_FILE_F_PATH_OFFSET` and `VIVARIUM_DENTRY_D_NAME_OFFSET` if auto-detection fails.
|
|
132
192
|
- `vivariumd` also prints `bpf_trace_printk` lines (`vivarium: pid=... path=...`) to its own logs.
|
|
133
193
|
|
|
134
194
|
## Contributing
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "tmpdir"
|
|
5
|
+
require "vivarium"
|
|
6
|
+
|
|
7
|
+
# Usage:
|
|
8
|
+
# 1) In another shell (root): sudo bundle exec vivariumd
|
|
9
|
+
# 2) Run this script: bundle exec ruby examples/execve_demo.rb
|
|
10
|
+
|
|
11
|
+
TMP_PREFIX = "vivarium-exec-demo"
|
|
12
|
+
|
|
13
|
+
def try_step(title)
|
|
14
|
+
puts "[exec-demo] #{title}"
|
|
15
|
+
yield
|
|
16
|
+
rescue StandardError => e
|
|
17
|
+
puts "[exec-demo] #{title} failed: #{e.class}: #{e.message}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
Dir.mktmpdir(TMP_PREFIX, "/tmp") do |dir|
|
|
21
|
+
output_path = File.join(dir, "execve-demo.out")
|
|
22
|
+
|
|
23
|
+
Vivarium.observe do
|
|
24
|
+
try_step("system echo with multiple args") do
|
|
25
|
+
system("/bin/echo", "hello", "from", "vivarium", out: File::NULL)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
try_step("spawn env with explicit argv") do
|
|
29
|
+
pid = Process.spawn(
|
|
30
|
+
"/usr/bin/env",
|
|
31
|
+
"env",
|
|
32
|
+
"printf",
|
|
33
|
+
"execve-demo\n",
|
|
34
|
+
out: output_path,
|
|
35
|
+
err: File::NULL
|
|
36
|
+
)
|
|
37
|
+
Process.wait(pid)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
try_step("spawn sleep with flag") do
|
|
41
|
+
pid = Process.spawn("/bin/sleep", "0")
|
|
42
|
+
Process.wait(pid)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
puts "[exec-demo] output file: #{output_path}" if File.exist?(output_path)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
puts "[exec-demo] done"
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "tmpdir"
|
|
6
|
+
require "vivarium"
|
|
7
|
+
|
|
8
|
+
# Usage:
|
|
9
|
+
# 1) In another shell (root): sudo bundle exec vivariumd
|
|
10
|
+
# 2) Run this script: bundle exec ruby examples/file_operation_demo.rb
|
|
11
|
+
|
|
12
|
+
TMP_PREFIX = "vivarium-file-demo"
|
|
13
|
+
|
|
14
|
+
def try_step(title)
|
|
15
|
+
puts "[file-demo] #{title}"
|
|
16
|
+
yield
|
|
17
|
+
rescue StandardError => e
|
|
18
|
+
puts "[file-demo] #{title} failed: #{e.class}: #{e.message}"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
Dir.mktmpdir(TMP_PREFIX, "/tmp") do |dir|
|
|
22
|
+
source_path = File.join(dir, "source.txt")
|
|
23
|
+
renamed_path = File.join(dir, "renamed.txt")
|
|
24
|
+
hardlink_path = File.join(dir, "hardlink.txt")
|
|
25
|
+
symlink_path = File.join(dir, "symlink.txt")
|
|
26
|
+
|
|
27
|
+
Vivarium.observe do
|
|
28
|
+
try_step("create source file") do
|
|
29
|
+
File.write(source_path, "vivarium sample\n")
|
|
30
|
+
File.read(source_path)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
try_step("directory listing") do
|
|
34
|
+
Dir.children(dir)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
try_step("rename file") do
|
|
38
|
+
File.rename(source_path, renamed_path)
|
|
39
|
+
File.read(renamed_path)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
try_step("create hardlink") do
|
|
43
|
+
File.link(renamed_path, hardlink_path)
|
|
44
|
+
File.read(hardlink_path)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
try_step("create symlink") do
|
|
48
|
+
File.symlink(renamed_path, symlink_path)
|
|
49
|
+
File.read(symlink_path)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
try_step("chmod file") do
|
|
53
|
+
File.chmod(0o640, renamed_path)
|
|
54
|
+
File.stat(renamed_path)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
try_step("list directory again") do
|
|
58
|
+
Dir.children(dir)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
FileUtils.rm_f(symlink_path)
|
|
63
|
+
FileUtils.rm_f(hardlink_path)
|
|
64
|
+
FileUtils.rm_f(renamed_path)
|
|
65
|
+
FileUtils.rm_f(source_path)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
puts "[file-demo] done"
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "socket"
|
|
5
|
+
require "vivarium"
|
|
6
|
+
|
|
7
|
+
# Usage:
|
|
8
|
+
# 1) In another shell (root): sudo bundle exec vivariumd
|
|
9
|
+
# 2) Run this script: bundle exec ruby examples/network_client_demo.rb
|
|
10
|
+
|
|
11
|
+
def try_step(title)
|
|
12
|
+
puts "[client] #{title}"
|
|
13
|
+
yield
|
|
14
|
+
rescue StandardError => e
|
|
15
|
+
puts "[client] #{title} failed: #{e.class}: #{e.message}"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
Vivarium.observe do
|
|
19
|
+
# Likely emits sock_connect and dns_req via resolver traffic.
|
|
20
|
+
try_step("system: DNS lookup") do
|
|
21
|
+
system("getent hosts example.com >/dev/null 2>&1 || true")
|
|
22
|
+
system("getent hosts unknown.example.com >/dev/null 2>&1 || true")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Likely emits sock_connect through HTTPS connection attempts.
|
|
26
|
+
try_step("system: curl") do
|
|
27
|
+
system("curl -I https://example.com >/dev/null 2>&1 || true")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# ICMP example (may require CAP_NET_RAW / root depending on environment).
|
|
31
|
+
try_step("system: ping") do
|
|
32
|
+
system("ping -c 1 example.com >/dev/null 2>&1 || true")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Explicit connect path.
|
|
36
|
+
try_step("Ruby TCP connect") do
|
|
37
|
+
sock = TCPSocket.new("example.com", 80)
|
|
38
|
+
sock.close
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Raw DNS query payload, useful for dns_req decode testing.
|
|
42
|
+
try_step("Ruby UDP DNS query") do
|
|
43
|
+
dns_query = "\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00" +
|
|
44
|
+
"\x09udp-query\x07example\x03com\x00" +
|
|
45
|
+
"\x00\x01\x00\x01"
|
|
46
|
+
|
|
47
|
+
udp = UDPSocket.new
|
|
48
|
+
begin
|
|
49
|
+
udp.connect("127.0.0.53", 53)
|
|
50
|
+
rescue StandardError
|
|
51
|
+
udp.connect("8.8.8.8", 53)
|
|
52
|
+
end
|
|
53
|
+
udp.send(dns_query, 0)
|
|
54
|
+
udp.close
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Explicit sendto path for DNS payload visibility.
|
|
58
|
+
try_step("Ruby UDP sendto DNS query") do
|
|
59
|
+
dns_query = "\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00" +
|
|
60
|
+
"\x06sendto\x07example\x03com\x00" +
|
|
61
|
+
"\x00\x01\x00\x01"
|
|
62
|
+
|
|
63
|
+
udp = UDPSocket.new
|
|
64
|
+
udp.send(dns_query, 0, "127.0.0.53", 53)
|
|
65
|
+
udp.close
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Intentionally unusual socket type to trigger odd_socket.
|
|
69
|
+
try_step("Ruby odd socket attempt") do
|
|
70
|
+
af_packet = Socket.const_defined?(:AF_PACKET) ? Socket::AF_PACKET : 17
|
|
71
|
+
raw = Socket.new(af_packet, Socket::SOCK_RAW, 0)
|
|
72
|
+
raw.close
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
puts "[client] done"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "vivarium"
|
|
5
|
+
|
|
6
|
+
# Usage:
|
|
7
|
+
# 1) In another shell (root): sudo bundle exec vivariumd
|
|
8
|
+
# 2) Run this script: bundle exec ruby examples/privilege_event_demo.rb
|
|
9
|
+
|
|
10
|
+
def try_step(title)
|
|
11
|
+
puts "[priv-demo] #{title}"
|
|
12
|
+
yield
|
|
13
|
+
rescue StandardError => e
|
|
14
|
+
puts "[priv-demo] #{title} failed: #{e.class}: #{e.message}"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
Vivarium.observe do
|
|
18
|
+
try_step("attempt setuid(0)") do
|
|
19
|
+
Process::UID.change_privilege(0)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
try_step("attempt setgid(0)") do
|
|
23
|
+
Process::GID.change_privilege(0)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
try_step("attempt opening /etc/shadow") do
|
|
27
|
+
File.read("/etc/shadow")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
try_step("exec setuid-related binary") do
|
|
31
|
+
pid = Process.spawn("/usr/bin/sudo", "-n", "true", out: File::NULL, err: File::NULL)
|
|
32
|
+
Process.wait(pid)
|
|
33
|
+
rescue Errno::ENOENT
|
|
34
|
+
puts "[priv-demo] sudo not found; skipped"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
puts "[priv-demo] done"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "vivarium"
|
|
5
|
+
|
|
6
|
+
# Usage:
|
|
7
|
+
# 1) In another shell (root): sudo bundle exec vivariumd
|
|
8
|
+
# 2) Run this script: bundle exec ruby examples/signal_kill_demo.rb
|
|
9
|
+
|
|
10
|
+
def try_step(title)
|
|
11
|
+
puts "[signal-demo] #{title}"
|
|
12
|
+
yield
|
|
13
|
+
rescue StandardError => e
|
|
14
|
+
puts "[signal-demo] #{title} failed: #{e.class}: #{e.message}"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
child_pid = nil
|
|
18
|
+
|
|
19
|
+
Vivarium.observe do
|
|
20
|
+
try_step("fork child process") do
|
|
21
|
+
child_pid = fork do
|
|
22
|
+
trap("TERM") { exit!(0) }
|
|
23
|
+
loop { sleep 1 }
|
|
24
|
+
end
|
|
25
|
+
puts "[signal-demo] child pid=#{child_pid}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
try_step("send TERM signal to child") do
|
|
29
|
+
sleep 0.1
|
|
30
|
+
Process.kill("TERM", child_pid)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
try_step("wait child process") do
|
|
34
|
+
Process.wait(child_pid)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
puts "[signal-demo] done"
|
data/lib/vivarium/logger.rb
CHANGED
|
@@ -5,6 +5,8 @@ require "json"
|
|
|
5
5
|
module Vivarium
|
|
6
6
|
class Logger
|
|
7
7
|
FORMATS = %i[human json].freeze
|
|
8
|
+
ANSI_RED = "\e[31m"
|
|
9
|
+
ANSI_RESET = "\e[0m"
|
|
8
10
|
|
|
9
11
|
# dest: IO object or file path string
|
|
10
12
|
# format: :human or :json
|
|
@@ -45,7 +47,9 @@ module Vivarium
|
|
|
45
47
|
@io.puts "[vivarium] #{events.size} event(s) at #{tp.defined_class}##{tp.method_id} (#{tp.event})"
|
|
46
48
|
@io.puts " location: #{tp.path}:#{tp.lineno}"
|
|
47
49
|
events.each do |event|
|
|
48
|
-
|
|
50
|
+
severity = event.respond_to?(:severity) ? event.severity : Vivarium.event_severity(event.event_name)
|
|
51
|
+
line = " ktime_ns=#{event.ktime_ns} pid=#{event.pid} severity=#{severity} #{event.event_name} payload=#{Vivarium.render_event_payload(event)}"
|
|
52
|
+
@io.puts(severity == "high" ? "#{ANSI_RED}#{line}#{ANSI_RESET}" : line)
|
|
49
53
|
end
|
|
50
54
|
@io.puts " stack:"
|
|
51
55
|
stack.each do |loc|
|
|
@@ -59,7 +63,15 @@ module Vivarium
|
|
|
59
63
|
event: tp.event.to_s,
|
|
60
64
|
path: tp.path,
|
|
61
65
|
lineno: tp.lineno,
|
|
62
|
-
events: events.map
|
|
66
|
+
events: events.map do |e|
|
|
67
|
+
{
|
|
68
|
+
ktime_ns: e.ktime_ns,
|
|
69
|
+
pid: e.pid,
|
|
70
|
+
severity: (e.respond_to?(:severity) ? e.severity : Vivarium.event_severity(e.event_name)),
|
|
71
|
+
event_name: e.event_name,
|
|
72
|
+
payload: Vivarium.render_event_payload(e)
|
|
73
|
+
}
|
|
74
|
+
end,
|
|
63
75
|
stack: stack.map { |loc| "#{loc.path}:#{loc.lineno}:in #{loc.base_label}" }
|
|
64
76
|
}
|
|
65
77
|
@io.puts JSON.generate(entry)
|
data/lib/vivarium/version.rb
CHANGED