process_tail 0.0.4 → 0.0.5
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/.gitignore +2 -0
- data/README.md +9 -5
- data/bin/process_tail +2 -8
- data/ext/process_tail/extconf.rb +31 -10
- data/ext/process_tail/process_tail.c +76 -218
- data/ext/process_tail/process_tail.h +31 -0
- data/ext/process_tail/pt_dtrace.c +194 -0
- data/ext/process_tail/pt_dtrace.h +10 -0
- data/ext/process_tail/pt_ptrace.c +234 -0
- data/ext/process_tail/pt_ptrace.h +8 -0
- data/ext/process_tail/pt_tracee.c +95 -0
- data/ext/process_tail/pt_tracee.h +20 -0
- data/lib/process_tail.rb +80 -60
- data/lib/process_tail/process_tail.d +77 -0
- data/lib/process_tail/version.rb +1 -1
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 162ae595f4fb2f2ae422ebfe8a302eac30ae9646
|
4
|
+
data.tar.gz: 66d884803130a2e7a1f0852be873b308f0d914a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 39872b3eb28cea4a41f3299633e7c497e5cd01526f7f4670679efeb54a13cf362f0b6597fc26b6ff7f449f7fafbe443822768b54b3a502f8724f13abd0f88ba2
|
7
|
+
data.tar.gz: ba405708d7b4627c94a3ce9265947db64c7293f78f02d2b2674a1a25cb700234a692d2a79689f68e3ed47a07fd12511a4568dac6ef667d2a09e7053d6dd8b79a
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -5,12 +5,12 @@ https://github.com/hibariya/process_tail
|
|
5
5
|
## Description
|
6
6
|
|
7
7
|
ProcessTail traces other process' write(2) system call and copy its buffer.
|
8
|
-
So
|
8
|
+
So we can get other process outputs.
|
9
9
|
|
10
10
|
## Problems
|
11
11
|
|
12
|
-
*
|
13
|
-
*
|
12
|
+
* SEGV occurs infrequently
|
13
|
+
* Windows is not supported
|
14
14
|
|
15
15
|
## Installation
|
16
16
|
|
@@ -30,11 +30,15 @@ Or install it yourself as:
|
|
30
30
|
|
31
31
|
## Usage
|
32
32
|
|
33
|
+
On Mac OSX, you'll have to use `sudo`.
|
34
|
+
|
33
35
|
### Get outputs as an IO object
|
34
36
|
|
35
37
|
```ruby
|
36
|
-
ProcessTail.
|
37
|
-
puts
|
38
|
+
ProcessTail.each pid do |tid, fd, str|
|
39
|
+
puts tid # pid or tid of this process (thread)
|
40
|
+
puts fd # File descripter number
|
41
|
+
puts str # Output string
|
38
42
|
end
|
39
43
|
```
|
40
44
|
|
data/bin/process_tail
CHANGED
@@ -4,12 +4,6 @@ require 'process_tail'
|
|
4
4
|
|
5
5
|
$stdout.sync = true
|
6
6
|
|
7
|
-
ProcessTail.
|
8
|
-
|
9
|
-
while c = io.getc
|
10
|
-
print c
|
11
|
-
end
|
12
|
-
rescue IOError
|
13
|
-
# NOP
|
14
|
-
end
|
7
|
+
ProcessTail.each Integer(ARGV[0]) do |tid, fd, str|
|
8
|
+
print str if fd == 1 || fd == 2
|
15
9
|
end
|
data/ext/process_tail/extconf.rb
CHANGED
@@ -1,17 +1,38 @@
|
|
1
1
|
require 'mkmf'
|
2
2
|
|
3
|
-
|
4
|
-
abort "missing #{h}" unless have_header(h)
|
5
|
-
end
|
3
|
+
objs = []
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
end
|
5
|
+
-> {
|
6
|
+
return unless have_library('dtrace', 'dtrace_open', 'dtrace.h')
|
10
7
|
|
11
|
-
|
12
|
-
abort "missing #{c}" unless have_const(c, 'sys/ptrace.h')
|
13
|
-
end
|
8
|
+
have_header 'dtrace.h'
|
14
9
|
|
15
|
-
|
10
|
+
objs << 'pt_dtrace.o'
|
11
|
+
}.()
|
16
12
|
|
13
|
+
-> {
|
14
|
+
return unless %w(orig_rax rdi rdx rsi rdx).all? {|m|
|
15
|
+
have_struct_member('struct user_regs_struct', m, 'sys/user.h')
|
16
|
+
}
|
17
|
+
|
18
|
+
return unless %w(sys/syscall.h sys/types.h sys/user.h sys/wait.h).all? {|h|
|
19
|
+
have_header(h)
|
20
|
+
}
|
21
|
+
|
22
|
+
return unless have_const('__WALL', 'sys/wait.h')
|
23
|
+
|
24
|
+
return unless %w(PTRACE_O_TRACESYSGOOD PTRACE_O_TRACEFORK PTRACE_O_TRACEVFORK PTRACE_O_TRACECLONE).all? {|c|
|
25
|
+
have_const(c, 'sys/ptrace.h')
|
26
|
+
}
|
27
|
+
|
28
|
+
have_header 'sys/ptrace.h'
|
29
|
+
|
30
|
+
objs << 'pt_ptrace.o'
|
31
|
+
}.()
|
32
|
+
|
33
|
+
abort 'No available libraries' if objs.empty?
|
34
|
+
|
35
|
+
$objs = %w(process_tail.o pt_tracee.o) + objs
|
36
|
+
|
37
|
+
create_header
|
17
38
|
create_makefile 'process_tail/process_tail'
|
@@ -1,268 +1,126 @@
|
|
1
|
-
#include <stdlib.h>
|
2
|
-
#include <string.h>
|
3
|
-
#include <sys/ptrace.h>
|
4
|
-
#include <sys/syscall.h>
|
5
1
|
#include <sys/types.h>
|
6
|
-
#include <sys/user.h>
|
7
|
-
#include <sys/wait.h>
|
8
2
|
#include <ruby.h>
|
9
|
-
#include
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
typedef struct {
|
19
|
-
int *status;
|
20
|
-
pid_t pid;
|
21
|
-
} pt_wait_args_t;
|
22
|
-
|
23
|
-
typedef struct {
|
24
|
-
unsigned int fd;
|
25
|
-
pt_tracee_t *tracee;
|
26
|
-
VALUE *wait_queue;
|
27
|
-
} pt_loop_args_t;
|
3
|
+
#include "extconf.h"
|
4
|
+
#include "process_tail.h"
|
5
|
+
#include "pt_tracee.h"
|
6
|
+
#ifdef HAVE_SYS_PTRACE_H
|
7
|
+
#include "pt_ptrace.h"
|
8
|
+
#endif
|
9
|
+
#ifdef HAVE_DTRACE_H
|
10
|
+
#include "pt_dtrace.h"
|
11
|
+
#endif
|
28
12
|
|
29
13
|
static void
|
30
|
-
|
31
|
-
{
|
32
|
-
tracee->pid = 0;
|
33
|
-
tracee->activated = 0;
|
34
|
-
tracee->dead = 0;
|
35
|
-
tracee->next = NULL;
|
36
|
-
}
|
37
|
-
|
38
|
-
static pt_tracee_t *
|
39
|
-
pt_tracee_add(pt_tracee_t **headpp, pid_t pid)
|
14
|
+
pt_process_tail_mark(pt_process_tail_t *ptp)
|
40
15
|
{
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
exit(EXIT_FAILURE);
|
45
|
-
}
|
46
|
-
|
47
|
-
pt_tracee_initialize(tracee);
|
48
|
-
tracee->pid = pid;
|
49
|
-
tracee->next = (struct pt_tracee *)*headpp;
|
50
|
-
|
51
|
-
(*headpp) = tracee;
|
52
|
-
|
53
|
-
return tracee;
|
16
|
+
rb_gc_mark(ptp->proc);
|
17
|
+
rb_gc_mark(ptp->trace_thread);
|
18
|
+
rb_gc_mark(ptp->wait_queue);
|
54
19
|
}
|
55
20
|
|
56
|
-
static
|
57
|
-
|
21
|
+
static VALUE
|
22
|
+
pt_process_tail_alloc(VALUE klass)
|
58
23
|
{
|
59
|
-
|
60
|
-
pt_tracee_t *current = headp;
|
61
|
-
|
62
|
-
while (current) {
|
63
|
-
if (current->pid == pid) {
|
64
|
-
return current;
|
65
|
-
}
|
24
|
+
pt_process_tail_t *ptp = ALLOC(pt_process_tail_t);
|
66
25
|
|
67
|
-
|
68
|
-
}
|
69
|
-
|
70
|
-
return pt_tracee_add(headpp, pid);
|
26
|
+
return Data_Wrap_Struct(klass, pt_process_tail_mark, RUBY_DEFAULT_FREE, ptp);
|
71
27
|
}
|
72
28
|
|
73
|
-
static
|
74
|
-
|
29
|
+
static VALUE
|
30
|
+
pt_process_tail_initialize(VALUE self, VALUE pidv, VALUE fdv)
|
75
31
|
{
|
76
|
-
|
32
|
+
pt_process_tail_t *ptp;
|
77
33
|
|
78
|
-
|
79
|
-
|
80
|
-
return 0;
|
81
|
-
}
|
82
|
-
|
83
|
-
current = current->next;
|
34
|
+
if (!rb_block_given_p()) {
|
35
|
+
rb_raise(rb_eArgError, "no block given");
|
84
36
|
}
|
85
37
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
static void
|
90
|
-
pt_tracee_free(pt_tracee_t **headpp)
|
91
|
-
{
|
92
|
-
pt_tracee_t *tracee = *headpp;
|
93
|
-
|
94
|
-
if (tracee) {
|
95
|
-
pt_tracee_free(&tracee->next);
|
38
|
+
Check_Type(pidv, T_FIXNUM);
|
39
|
+
Check_Type(fdv, T_FIXNUM);
|
96
40
|
|
97
|
-
|
98
|
-
tracee = NULL;
|
99
|
-
}
|
100
|
-
}
|
41
|
+
Data_Get_Struct(self, pt_process_tail_t, ptp);
|
101
42
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
43
|
+
ptp->pid = (pid_t)NUM2INT(pidv);
|
44
|
+
ptp->fd = (unsigned int)NUM2INT(fdv);
|
45
|
+
ptp->proc = rb_block_proc();
|
46
|
+
ptp->trace_thread = (VALUE)NULL;
|
47
|
+
ptp->parent_thread = (VALUE)NULL;
|
48
|
+
ptp->wait_queue = (VALUE)NULL;
|
49
|
+
ptp->tracee = NULL;
|
106
50
|
|
107
|
-
|
108
|
-
|
109
|
-
return NULL;
|
51
|
+
return Qnil;
|
110
52
|
}
|
111
53
|
|
112
|
-
static
|
113
|
-
|
54
|
+
static VALUE
|
55
|
+
pt_process_tail_pid_reader(VALUE self)
|
114
56
|
{
|
115
|
-
|
116
|
-
long data;
|
57
|
+
pt_process_tail_t *ptp;
|
117
58
|
|
118
|
-
|
119
|
-
data = ptrace(PTRACE_PEEKDATA, pid, addr + i);
|
59
|
+
Data_Get_Struct(self, pt_process_tail_t, ptp);
|
120
60
|
|
121
|
-
|
122
|
-
}
|
123
|
-
|
124
|
-
string[length] = '\0';
|
125
|
-
}
|
126
|
-
|
127
|
-
static void
|
128
|
-
pt_ptrace_syscall(pid_t pid, long signal)
|
129
|
-
{
|
130
|
-
ptrace(PTRACE_SYSCALL, pid, 0, signal == SIGTRAP ? 0 : signal);
|
131
|
-
|
132
|
-
rb_thread_schedule();
|
61
|
+
return INT2NUM((int)ptp->pid);
|
133
62
|
}
|
134
63
|
|
135
64
|
static VALUE
|
136
|
-
|
65
|
+
pt_process_tail_fd_reader(VALUE self)
|
137
66
|
{
|
138
|
-
|
139
|
-
|
140
|
-
char *string = NULL;
|
141
|
-
int syscall = 0;
|
142
|
-
int status;
|
143
|
-
pid_t pid;
|
144
|
-
pt_tracee_t *tracee;
|
145
|
-
pt_wait_args_t pt_wait_args = {&status};
|
146
|
-
|
147
|
-
// NOTE: system call: orig_rax, arguments: rdi, rsi, rdx, r10, r8, r9
|
148
|
-
struct user_regs_struct regs;
|
149
|
-
|
150
|
-
while (1) {
|
151
|
-
rb_thread_call_without_gvl(pt_wait, &pt_wait_args, RUBY_UBF_PROCESS, NULL);
|
152
|
-
|
153
|
-
pid = pt_wait_args.pid;
|
154
|
-
tracee = pt_tracee_find_or_add(&args->tracee, pid);
|
67
|
+
pt_process_tail_t *ptp;
|
155
68
|
|
156
|
-
|
157
|
-
tracee->activated = 1;
|
158
|
-
rb_funcall(*args->wait_queue, rb_intern("enq"), 1, INT2FIX((int)pid));
|
69
|
+
Data_Get_Struct(self, pt_process_tail_t, ptp);
|
159
70
|
|
160
|
-
|
161
|
-
pt_ptrace_syscall(pid, 0);
|
162
|
-
|
163
|
-
continue;
|
164
|
-
}
|
165
|
-
|
166
|
-
if (WIFEXITED(status) || WIFSIGNALED(status)) {
|
167
|
-
tracee->dead = 1;
|
168
|
-
|
169
|
-
if (pt_tracee_wipedoutq(args->tracee)) {
|
170
|
-
return Qnil;
|
171
|
-
}
|
172
|
-
|
173
|
-
continue;
|
174
|
-
}
|
175
|
-
|
176
|
-
if (!(WIFSTOPPED(status) && WSTOPSIG(status) & 0x80)) {
|
177
|
-
pt_ptrace_syscall(pid, WSTOPSIG(status));
|
178
|
-
|
179
|
-
continue;
|
180
|
-
}
|
181
|
-
|
182
|
-
ptrace(PTRACE_GETREGS, pid, 0, ®s);
|
183
|
-
|
184
|
-
if (regs.orig_rax != SYS_write) {
|
185
|
-
pt_ptrace_syscall(pid, 0);
|
186
|
-
|
187
|
-
continue;
|
188
|
-
}
|
189
|
-
|
190
|
-
syscall = 1 - syscall;
|
191
|
-
if (syscall && (args->fd == 0 || regs.rdi == args->fd)) {
|
192
|
-
string = malloc(regs.rdx + sizeof(long));
|
193
|
-
|
194
|
-
if (string == NULL) {
|
195
|
-
exit(EXIT_FAILURE);
|
196
|
-
}
|
197
|
-
|
198
|
-
pt_read_data(pid, regs.rsi, string, regs.rdx);
|
199
|
-
|
200
|
-
rb_yield(rb_ary_new_from_args(3, rb_str_new_cstr(string), INT2FIX((int)pid), INT2FIX(args->fd)));
|
201
|
-
|
202
|
-
free(string);
|
203
|
-
}
|
204
|
-
|
205
|
-
pt_ptrace_syscall(pid, 0);
|
206
|
-
}
|
207
|
-
|
208
|
-
return Qnil;
|
71
|
+
return INT2NUM((int)ptp->fd);
|
209
72
|
}
|
210
73
|
|
211
74
|
static VALUE
|
212
|
-
|
75
|
+
pt_process_tail_trace_thread_reader(VALUE self)
|
213
76
|
{
|
214
|
-
|
215
|
-
|
216
|
-
Check_Type(pidv, T_FIXNUM);
|
217
|
-
|
218
|
-
if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) {
|
219
|
-
rb_sys_fail("PTRACE_ATTACH");
|
77
|
+
pt_process_tail_t *ptp;
|
220
78
|
|
221
|
-
|
222
|
-
}
|
79
|
+
Data_Get_Struct(self, pt_process_tail_t, ptp);
|
223
80
|
|
224
|
-
return
|
81
|
+
return ptp->trace_thread;
|
225
82
|
}
|
226
83
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
ptrace(PTRACE_DETACH, (pid_t)FIX2INT(pidv), NULL, NULL);
|
84
|
+
VALUE ProcessTail;
|
85
|
+
VALUE ProcessTail_Tracer;
|
86
|
+
VALUE ProcessTail_StopTracing;
|
87
|
+
VALUE ProcessTail_TraceError;
|
233
88
|
|
234
|
-
|
89
|
+
void
|
90
|
+
pt_lock_trace(void)
|
91
|
+
{
|
92
|
+
rb_funcall(PT_TRACE_LOCK, rb_intern("lock"), 0);
|
235
93
|
}
|
236
94
|
|
237
|
-
|
238
|
-
|
95
|
+
void
|
96
|
+
pt_unlock_trace(void)
|
239
97
|
{
|
240
|
-
|
241
|
-
|
242
|
-
pt_tracee_free(&tracee);
|
243
|
-
|
244
|
-
return Qnil;
|
98
|
+
rb_funcall(PT_TRACE_LOCK, rb_intern("unlock"), 0);
|
245
99
|
}
|
246
100
|
|
247
|
-
|
248
|
-
|
101
|
+
void
|
102
|
+
Init_process_tail(void)
|
249
103
|
{
|
250
|
-
|
251
|
-
pt_loop_args_t loop_args = {FIX2INT(fdv), tracee, &wait_queue};
|
104
|
+
ProcessTail = rb_define_module("ProcessTail");
|
252
105
|
|
253
|
-
|
106
|
+
ProcessTail_StopTracing = rb_define_class_under(ProcessTail, "StopTracing", rb_eStandardError);
|
107
|
+
ProcessTail_TraceError = rb_define_class_under(ProcessTail, "TraceError", rb_eStandardError);
|
254
108
|
|
255
|
-
|
109
|
+
ProcessTail_Tracer = rb_define_class_under(ProcessTail, "Tracer", rb_cObject);
|
110
|
+
rb_define_const(ProcessTail_Tracer, "TRACE_LOCK", rb_class_new_instance(0, NULL, rb_path2class("Mutex")));
|
256
111
|
|
257
|
-
|
258
|
-
|
112
|
+
rb_define_alloc_func(ProcessTail_Tracer, pt_process_tail_alloc);
|
113
|
+
rb_define_private_method(ProcessTail_Tracer, "initialize", pt_process_tail_initialize, 2);
|
259
114
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
115
|
+
rb_define_method(ProcessTail_Tracer, "pid", pt_process_tail_pid_reader, 0);
|
116
|
+
rb_define_method(ProcessTail_Tracer, "fd", pt_process_tail_fd_reader, 0);
|
117
|
+
rb_define_method(ProcessTail_Tracer, "trace_thread", pt_process_tail_trace_thread_reader, 0);
|
118
|
+
|
119
|
+
#ifdef HAVE_SYS_PTRACE_H
|
120
|
+
rb_define_private_method(ProcessTail_Tracer, "ptrace_attach", pt_ptrace_attach, 0);
|
121
|
+
#endif
|
264
122
|
|
265
|
-
|
266
|
-
rb_define_private_method(
|
267
|
-
|
123
|
+
#ifdef HAVE_DTRACE_H
|
124
|
+
rb_define_private_method(ProcessTail_Tracer, "dtrace_attach", pt_dtrace_attach, 0);
|
125
|
+
#endif
|
268
126
|
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#ifndef PROCESS_TAIL_H
|
2
|
+
#define PROCESS_TAIL_H
|
3
|
+
#include <sys/types.h>
|
4
|
+
#include <ruby.h>
|
5
|
+
#include "extconf.h"
|
6
|
+
#include "pt_tracee.h"
|
7
|
+
|
8
|
+
#define PT_TRACE_LOCK (rb_const_get(ProcessTail_Tracer, rb_intern("TRACE_LOCK")))
|
9
|
+
|
10
|
+
extern VALUE ProcessTail;
|
11
|
+
extern VALUE ProcessTail_Tracer;
|
12
|
+
extern VALUE ProcessTail_StopTracing;
|
13
|
+
extern VALUE ProcessTail_TraceError;
|
14
|
+
|
15
|
+
typedef struct {
|
16
|
+
pid_t pid;
|
17
|
+
unsigned int fd;
|
18
|
+
VALUE proc;
|
19
|
+
VALUE trace_thread;
|
20
|
+
VALUE parent_thread;
|
21
|
+
VALUE wait_queue;
|
22
|
+
pt_tracee_t *tracee;
|
23
|
+
void *data;
|
24
|
+
} pt_process_tail_t;
|
25
|
+
|
26
|
+
void pt_lock_trace(void);
|
27
|
+
void pt_unlock_trace(void);
|
28
|
+
|
29
|
+
void Init_process_tail(void);
|
30
|
+
|
31
|
+
#endif
|
@@ -0,0 +1,194 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
#include <dtrace.h>
|
3
|
+
#include <ruby.h>
|
4
|
+
#include <ruby/thread.h>
|
5
|
+
#include "process_tail.h"
|
6
|
+
#include "pt_dtrace.h"
|
7
|
+
|
8
|
+
static int
|
9
|
+
probe_consumer(const dtrace_probedata_t *data, void *argp)
|
10
|
+
{
|
11
|
+
pt_process_tail_t *pt;
|
12
|
+
|
13
|
+
dtrace_eprobedesc_t *edesc = data->dtpda_edesc;
|
14
|
+
dtrace_recdesc_t *rec;
|
15
|
+
caddr_t addr;
|
16
|
+
|
17
|
+
int i;
|
18
|
+
VALUE arg;
|
19
|
+
VALUE args = rb_ary_new_capa(3);
|
20
|
+
|
21
|
+
Data_Get_Struct((VALUE)argp, pt_process_tail_t, pt);
|
22
|
+
|
23
|
+
for (i = 0; i < edesc->dtepd_nrecs; i++) {
|
24
|
+
arg = 0;
|
25
|
+
rec = &edesc->dtepd_rec[i];
|
26
|
+
|
27
|
+
if (rec->dtrd_size > 0) {
|
28
|
+
addr = data->dtpda_data + rec->dtrd_offset;
|
29
|
+
|
30
|
+
switch(rec->dtrd_action) {
|
31
|
+
case DTRACEACT_DIFEXPR:
|
32
|
+
switch(rec->dtrd_size) {
|
33
|
+
case 1:
|
34
|
+
arg = INT2NUM((int)(*((uint8_t *)addr)));
|
35
|
+
break;
|
36
|
+
case 2:
|
37
|
+
arg = INT2NUM((int)(*((uint16_t *)addr)));
|
38
|
+
break;
|
39
|
+
case 4:
|
40
|
+
arg = INT2NUM(*((int32_t *)addr));
|
41
|
+
break;
|
42
|
+
case 8:
|
43
|
+
arg = LL2NUM(*((int64_t *)addr));
|
44
|
+
break;
|
45
|
+
default:
|
46
|
+
arg = rb_str_new_cstr((char *)addr);
|
47
|
+
}
|
48
|
+
|
49
|
+
rb_ary_push(args, arg);
|
50
|
+
break;
|
51
|
+
default:
|
52
|
+
break;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
rb_proc_call(pt->proc, args);
|
58
|
+
|
59
|
+
return (DTRACE_CONSUME_THIS);
|
60
|
+
}
|
61
|
+
|
62
|
+
static void *
|
63
|
+
pt_dtrace_sleep(void *argp)
|
64
|
+
{
|
65
|
+
dtrace_hdl_t *hdl = (dtrace_hdl_t *)argp;
|
66
|
+
|
67
|
+
dtrace_sleep(hdl);
|
68
|
+
|
69
|
+
return NULL;
|
70
|
+
}
|
71
|
+
|
72
|
+
static VALUE
|
73
|
+
pt_loop(VALUE self)
|
74
|
+
{
|
75
|
+
pt_process_tail_t *pt;
|
76
|
+
dtrace_hdl_t *hdl;
|
77
|
+
|
78
|
+
VALUE dscriptv = rb_funcall(self, rb_intern("generate_dscript"), 0);
|
79
|
+
dtrace_prog_t *program;
|
80
|
+
dtrace_proginfo_t *proginfo;
|
81
|
+
|
82
|
+
FILE *devnull = fopen("/dev/null", "w");
|
83
|
+
int activated = 0;
|
84
|
+
int done = 0;
|
85
|
+
|
86
|
+
Data_Get_Struct(self, pt_process_tail_t, pt);
|
87
|
+
|
88
|
+
hdl = PT_DTRACE_HDL(pt);
|
89
|
+
|
90
|
+
dtrace_setopt(hdl, "stacksymbols", "enabled");
|
91
|
+
dtrace_setopt(hdl, "bufsize", "4m");
|
92
|
+
dtrace_setopt(hdl, "quiet", 0);
|
93
|
+
|
94
|
+
program = dtrace_program_strcompile(hdl, StringValueCStr(dscriptv), DTRACE_PROBESPEC_NAME, DTRACE_C_PSPEC, 0, NULL);
|
95
|
+
|
96
|
+
if (!program) {
|
97
|
+
rb_raise(ProcessTail_TraceError, "dtrace_program_strcompile: %s", dtrace_errmsg(hdl, dtrace_errno(hdl)));
|
98
|
+
return Qnil;
|
99
|
+
}
|
100
|
+
|
101
|
+
if (dtrace_program_exec(hdl, program, proginfo) < 0) {
|
102
|
+
rb_raise(ProcessTail_TraceError, "dtrace_program_exec: %s", dtrace_errmsg(hdl, dtrace_errno(hdl)));
|
103
|
+
return Qnil;
|
104
|
+
}
|
105
|
+
|
106
|
+
if (dtrace_go(hdl) < 0) {
|
107
|
+
rb_raise(ProcessTail_TraceError, "dtrace_go: %s", dtrace_errmsg(hdl, dtrace_errno(hdl)));
|
108
|
+
return Qnil;
|
109
|
+
}
|
110
|
+
|
111
|
+
do {
|
112
|
+
if (!done) {
|
113
|
+
if (!activated) {
|
114
|
+
rb_funcall(pt->wait_queue, rb_intern("enq"), 1, INT2NUM((int)pt->pid));
|
115
|
+
|
116
|
+
activated = 1;
|
117
|
+
}
|
118
|
+
|
119
|
+
rb_thread_call_without_gvl(pt_dtrace_sleep, hdl, RUBY_UBF_PROCESS, NULL);
|
120
|
+
}
|
121
|
+
|
122
|
+
if (done) {
|
123
|
+
if(dtrace_stop(hdl) == -1) {
|
124
|
+
rb_raise(ProcessTail_TraceError, "dtrace_stop: %s", dtrace_errmsg(hdl, dtrace_errno(hdl)));
|
125
|
+
return Qnil;
|
126
|
+
}
|
127
|
+
}
|
128
|
+
|
129
|
+
switch (dtrace_work(hdl, devnull, probe_consumer, NULL, (void *)self)) {
|
130
|
+
case DTRACE_WORKSTATUS_DONE:
|
131
|
+
done = 1;
|
132
|
+
break;
|
133
|
+
case DTRACE_WORKSTATUS_OKAY:
|
134
|
+
break;
|
135
|
+
default:
|
136
|
+
break;
|
137
|
+
}
|
138
|
+
|
139
|
+
rb_thread_schedule();
|
140
|
+
} while(!done);
|
141
|
+
|
142
|
+
return Qnil;
|
143
|
+
}
|
144
|
+
|
145
|
+
static VALUE
|
146
|
+
pt_finalize(VALUE self)
|
147
|
+
{
|
148
|
+
pt_process_tail_t *pt;
|
149
|
+
|
150
|
+
Data_Get_Struct(self, pt_process_tail_t, pt);
|
151
|
+
|
152
|
+
dtrace_close(PT_DTRACE_HDL(pt));
|
153
|
+
|
154
|
+
pt_unlock_trace();
|
155
|
+
rb_funcall(pt->parent_thread, rb_intern("raise"), 1, ProcessTail_StopTracing);
|
156
|
+
|
157
|
+
return Qnil;
|
158
|
+
}
|
159
|
+
|
160
|
+
static VALUE
|
161
|
+
pt_loop_thread(void *self)
|
162
|
+
{
|
163
|
+
pt_lock_trace();
|
164
|
+
|
165
|
+
rb_ensure(pt_loop, (VALUE)self, pt_finalize, (VALUE)self);
|
166
|
+
|
167
|
+
return Qnil;
|
168
|
+
}
|
169
|
+
|
170
|
+
VALUE
|
171
|
+
pt_dtrace_attach(VALUE self)
|
172
|
+
{
|
173
|
+
pt_process_tail_t *pt;
|
174
|
+
|
175
|
+
int error;
|
176
|
+
dtrace_hdl_t *hdl = dtrace_open(DTRACE_VERSION, 0, &error);
|
177
|
+
|
178
|
+
Data_Get_Struct(self, pt_process_tail_t, pt);
|
179
|
+
|
180
|
+
if (!hdl) {
|
181
|
+
rb_raise(ProcessTail_TraceError, "dtrace_open: %s", strerror(error));
|
182
|
+
return Qnil;
|
183
|
+
}
|
184
|
+
|
185
|
+
pt->tracee = NULL; // NOT USED
|
186
|
+
pt->data = (void *)hdl;
|
187
|
+
pt->wait_queue = rb_class_new_instance(0, NULL, rb_path2class("Queue"));
|
188
|
+
pt->parent_thread = rb_thread_current();
|
189
|
+
pt->trace_thread = rb_thread_create(pt_loop_thread, (void *)self);
|
190
|
+
|
191
|
+
rb_funcall(pt->wait_queue, rb_intern("deq"), 0);
|
192
|
+
|
193
|
+
return pt->trace_thread;
|
194
|
+
}
|
@@ -0,0 +1,234 @@
|
|
1
|
+
#include <stdlib.h>
|
2
|
+
#include <string.h>
|
3
|
+
#include <dirent.h>
|
4
|
+
#include <sys/ptrace.h>
|
5
|
+
#include <sys/syscall.h>
|
6
|
+
#include <sys/types.h>
|
7
|
+
#include <sys/user.h>
|
8
|
+
#include <sys/wait.h>
|
9
|
+
#include <ruby.h>
|
10
|
+
#include <ruby/thread.h>
|
11
|
+
#include "process_tail.h"
|
12
|
+
#include "pt_tracee.h"
|
13
|
+
#include "pt_ptrace.h"
|
14
|
+
|
15
|
+
typedef struct {
|
16
|
+
int *status;
|
17
|
+
pid_t pid;
|
18
|
+
} pt_wait_args_t;
|
19
|
+
|
20
|
+
static pt_tracee_t *
|
21
|
+
pt_extract_tracee_list(pid_t pid)
|
22
|
+
{
|
23
|
+
pt_tracee_t *tracee = NULL;
|
24
|
+
char procdir[sizeof("/proc/%d/task") + sizeof(int) * 3];
|
25
|
+
DIR *dir;
|
26
|
+
struct dirent *entry;
|
27
|
+
pid_t tid;
|
28
|
+
|
29
|
+
sprintf(procdir, "/proc/%d/task", pid);
|
30
|
+
dir = opendir(procdir);
|
31
|
+
|
32
|
+
while ((entry = readdir(dir)) != NULL) {
|
33
|
+
if (entry->d_fileno == 0) {
|
34
|
+
continue;
|
35
|
+
}
|
36
|
+
|
37
|
+
tid = (pid_t)atoi(entry->d_name);
|
38
|
+
|
39
|
+
if (tid <= 0) {
|
40
|
+
continue;
|
41
|
+
}
|
42
|
+
|
43
|
+
tracee = pt_tracee_find_or_add(&tracee, tid);
|
44
|
+
}
|
45
|
+
|
46
|
+
closedir(dir);
|
47
|
+
|
48
|
+
return tracee;
|
49
|
+
}
|
50
|
+
|
51
|
+
static void *
|
52
|
+
pt_wait(void *argv)
|
53
|
+
{
|
54
|
+
pt_wait_args_t *args = (pt_wait_args_t *)argv;
|
55
|
+
|
56
|
+
args->pid = waitpid(-1, args->status, __WALL);
|
57
|
+
|
58
|
+
return NULL;
|
59
|
+
}
|
60
|
+
|
61
|
+
static void
|
62
|
+
pt_read_data(pid_t pid, long addr, char *string, int length)
|
63
|
+
{
|
64
|
+
int i;
|
65
|
+
long data;
|
66
|
+
|
67
|
+
for (i = 0; i < length; i += sizeof(long)) {
|
68
|
+
data = ptrace(PTRACE_PEEKDATA, pid, addr + i);
|
69
|
+
|
70
|
+
memcpy(string + i, &data, sizeof(long));
|
71
|
+
}
|
72
|
+
|
73
|
+
string[length] = '\0';
|
74
|
+
}
|
75
|
+
|
76
|
+
static void
|
77
|
+
pt_ptrace_syscall(pid_t pid, long signal)
|
78
|
+
{
|
79
|
+
ptrace(PTRACE_SYSCALL, pid, 0, signal == SIGTRAP || signal == SIGSTOP ? 0 : signal);
|
80
|
+
|
81
|
+
rb_thread_schedule();
|
82
|
+
}
|
83
|
+
|
84
|
+
static int
|
85
|
+
pt_ptrace_attach_multi(pt_tracee_t *tracee)
|
86
|
+
{
|
87
|
+
do {
|
88
|
+
if (ptrace(PTRACE_ATTACH, tracee->pid, NULL, NULL) < 0) {
|
89
|
+
return 0;
|
90
|
+
}
|
91
|
+
} while ((tracee = tracee->next) != NULL);
|
92
|
+
|
93
|
+
return 1;
|
94
|
+
}
|
95
|
+
|
96
|
+
static VALUE
|
97
|
+
pt_loop(VALUE self)
|
98
|
+
{
|
99
|
+
pt_process_tail_t *pt;
|
100
|
+
|
101
|
+
pt_tracee_t *tracee;
|
102
|
+
|
103
|
+
pid_t pid;
|
104
|
+
int status;
|
105
|
+
pt_wait_args_t wait_args = {&status};
|
106
|
+
|
107
|
+
int syscall = 0;
|
108
|
+
char *string = NULL;
|
109
|
+
|
110
|
+
struct user_regs_struct regs; // NOTE: system call: orig_rax, arguments: rdi, rsi, rdx, r10, r8, r9
|
111
|
+
|
112
|
+
Data_Get_Struct(self, pt_process_tail_t, pt);
|
113
|
+
|
114
|
+
if (!pt_ptrace_attach_multi(pt->tracee)) {
|
115
|
+
rb_raise(ProcessTail_TraceError, "PTRACE_ATTACH");
|
116
|
+
|
117
|
+
return Qnil;
|
118
|
+
}
|
119
|
+
|
120
|
+
while (1) {
|
121
|
+
rb_thread_call_without_gvl(pt_wait, &wait_args, RUBY_UBF_PROCESS, NULL);
|
122
|
+
|
123
|
+
pid = wait_args.pid;
|
124
|
+
tracee = pt_tracee_find_or_add(&pt->tracee, pid);
|
125
|
+
|
126
|
+
if (tracee->activated == 0) {
|
127
|
+
tracee->activated = 1;
|
128
|
+
rb_funcall(pt->wait_queue, rb_intern("enq"), 1, INT2NUM((int)pid));
|
129
|
+
|
130
|
+
ptrace(PTRACE_SETOPTIONS, pid, 0,
|
131
|
+
PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE
|
132
|
+
);
|
133
|
+
|
134
|
+
pt_ptrace_syscall(pid, 0);
|
135
|
+
|
136
|
+
continue;
|
137
|
+
}
|
138
|
+
|
139
|
+
if (WIFEXITED(status) || WIFSIGNALED(status)) {
|
140
|
+
tracee->dead = 1;
|
141
|
+
|
142
|
+
if (pt_tracee_wipedoutq(pt->tracee)) {
|
143
|
+
return Qnil;
|
144
|
+
}
|
145
|
+
|
146
|
+
continue;
|
147
|
+
}
|
148
|
+
|
149
|
+
if (!(WIFSTOPPED(status) && WSTOPSIG(status) & 0x80)) {
|
150
|
+
pt_ptrace_syscall(pid, WSTOPSIG(status));
|
151
|
+
|
152
|
+
continue;
|
153
|
+
}
|
154
|
+
|
155
|
+
ptrace(PTRACE_GETREGS, pid, 0, ®s);
|
156
|
+
|
157
|
+
if (regs.orig_rax != SYS_write) {
|
158
|
+
pt_ptrace_syscall(pid, 0);
|
159
|
+
|
160
|
+
continue;
|
161
|
+
}
|
162
|
+
|
163
|
+
syscall = 1 - syscall;
|
164
|
+
if (syscall && (pt->fd == 0 || regs.rdi == pt->fd)) {
|
165
|
+
string = malloc(regs.rdx + sizeof(long));
|
166
|
+
|
167
|
+
if (string == NULL) {
|
168
|
+
exit(EXIT_FAILURE);
|
169
|
+
}
|
170
|
+
|
171
|
+
pt_read_data(pid, regs.rsi, string, regs.rdx);
|
172
|
+
|
173
|
+
rb_proc_call(pt->proc, rb_ary_new_from_args(3, INT2NUM((int)pid), INT2NUM(regs.rdi), rb_str_new_cstr(string)));
|
174
|
+
|
175
|
+
free(string);
|
176
|
+
}
|
177
|
+
|
178
|
+
pt_ptrace_syscall(pid, 0);
|
179
|
+
}
|
180
|
+
|
181
|
+
return Qnil;
|
182
|
+
}
|
183
|
+
|
184
|
+
static VALUE
|
185
|
+
pt_finalize(VALUE self)
|
186
|
+
{
|
187
|
+
pt_process_tail_t *pt;
|
188
|
+
Data_Get_Struct(self, pt_process_tail_t, pt);
|
189
|
+
|
190
|
+
ptrace(PTRACE_DETACH, pt->pid, NULL, NULL);
|
191
|
+
pt_tracee_free(&pt->tracee);
|
192
|
+
|
193
|
+
pt_unlock_trace();
|
194
|
+
rb_funcall(pt->parent_thread, rb_intern("raise"), 1, ProcessTail_StopTracing);
|
195
|
+
|
196
|
+
return Qnil;
|
197
|
+
}
|
198
|
+
|
199
|
+
static VALUE
|
200
|
+
pt_loop_thread(void *self)
|
201
|
+
{
|
202
|
+
pt_lock_trace();
|
203
|
+
|
204
|
+
rb_ensure(pt_loop, (VALUE)self, pt_finalize, (VALUE)self);
|
205
|
+
|
206
|
+
return Qnil;
|
207
|
+
}
|
208
|
+
|
209
|
+
static void
|
210
|
+
wait_queue_ntimes(VALUE wait_queue, int n)
|
211
|
+
{
|
212
|
+
int i;
|
213
|
+
|
214
|
+
for (i = 0; i < n; i++) {
|
215
|
+
rb_funcall(wait_queue, rb_intern("deq"), 0);
|
216
|
+
}
|
217
|
+
}
|
218
|
+
|
219
|
+
VALUE
|
220
|
+
pt_ptrace_attach(VALUE self)
|
221
|
+
{
|
222
|
+
pt_process_tail_t *pt;
|
223
|
+
|
224
|
+
Data_Get_Struct(self, pt_process_tail_t, pt);
|
225
|
+
|
226
|
+
pt->tracee = pt_extract_tracee_list(pt->pid);
|
227
|
+
pt->wait_queue = rb_class_new_instance(0, NULL, rb_path2class("Queue"));
|
228
|
+
pt->parent_thread = rb_thread_current();
|
229
|
+
pt->trace_thread = rb_thread_create(pt_loop_thread, (void *)self);
|
230
|
+
|
231
|
+
wait_queue_ntimes(pt->wait_queue, pt_tracee_length(pt->tracee));
|
232
|
+
|
233
|
+
return pt->trace_thread;
|
234
|
+
}
|
@@ -0,0 +1,95 @@
|
|
1
|
+
#include <sys/types.h>
|
2
|
+
#include <stdlib.h>
|
3
|
+
#include "pt_tracee.h"
|
4
|
+
|
5
|
+
void
|
6
|
+
pt_tracee_initialize(pt_tracee_t *tracee)
|
7
|
+
{
|
8
|
+
tracee->pid = 0;
|
9
|
+
tracee->activated = 0;
|
10
|
+
tracee->dead = 0;
|
11
|
+
tracee->next = NULL;
|
12
|
+
}
|
13
|
+
|
14
|
+
pt_tracee_t *
|
15
|
+
pt_tracee_add(pt_tracee_t **headpp, pid_t pid)
|
16
|
+
{
|
17
|
+
pt_tracee_t *tracee = malloc(sizeof(pt_tracee_t));
|
18
|
+
|
19
|
+
if (tracee == NULL) {
|
20
|
+
exit(EXIT_FAILURE);
|
21
|
+
}
|
22
|
+
|
23
|
+
pt_tracee_initialize(tracee);
|
24
|
+
tracee->pid = pid;
|
25
|
+
tracee->next = (struct pt_tracee *)*headpp;
|
26
|
+
|
27
|
+
(*headpp) = tracee;
|
28
|
+
|
29
|
+
return tracee;
|
30
|
+
}
|
31
|
+
|
32
|
+
pt_tracee_t *
|
33
|
+
pt_tracee_find_or_add(pt_tracee_t **headpp, pid_t pid)
|
34
|
+
{
|
35
|
+
pt_tracee_t *headp = *headpp;
|
36
|
+
pt_tracee_t *current = headp;
|
37
|
+
|
38
|
+
while (current) {
|
39
|
+
if (current->pid == pid) {
|
40
|
+
return current;
|
41
|
+
}
|
42
|
+
|
43
|
+
current = current->next;
|
44
|
+
}
|
45
|
+
|
46
|
+
return pt_tracee_add(headpp, pid);
|
47
|
+
}
|
48
|
+
|
49
|
+
int
|
50
|
+
pt_tracee_wipedoutq(pt_tracee_t *headp)
|
51
|
+
{
|
52
|
+
pt_tracee_t *current = headp;
|
53
|
+
|
54
|
+
while (current) {
|
55
|
+
if (current->dead == 0) {
|
56
|
+
return 0;
|
57
|
+
}
|
58
|
+
|
59
|
+
current = current->next;
|
60
|
+
}
|
61
|
+
|
62
|
+
return 1;
|
63
|
+
}
|
64
|
+
|
65
|
+
void
|
66
|
+
pt_tracee_free(pt_tracee_t **headpp)
|
67
|
+
{
|
68
|
+
pt_tracee_t *tracee = *headpp;
|
69
|
+
|
70
|
+
if (tracee) {
|
71
|
+
pt_tracee_free(&tracee->next);
|
72
|
+
|
73
|
+
free(tracee);
|
74
|
+
tracee = NULL;
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
int
|
79
|
+
pt_tracee_length(pt_tracee_t *tracee)
|
80
|
+
{
|
81
|
+
pt_tracee_t *current;
|
82
|
+
int length = 0;
|
83
|
+
|
84
|
+
if (tracee == NULL) {
|
85
|
+
return length;
|
86
|
+
}
|
87
|
+
|
88
|
+
current = tracee;
|
89
|
+
|
90
|
+
do {
|
91
|
+
length++;
|
92
|
+
} while ((current = current->next) != NULL);
|
93
|
+
|
94
|
+
return length;
|
95
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#ifndef PT_TRACEE_H
|
2
|
+
#define PT_TRACEE_H
|
3
|
+
|
4
|
+
#include <sys/types.h>
|
5
|
+
|
6
|
+
typedef struct pt_tracee {
|
7
|
+
pid_t pid;
|
8
|
+
int activated;
|
9
|
+
int dead;
|
10
|
+
struct pt_tracee *next;
|
11
|
+
} pt_tracee_t;
|
12
|
+
|
13
|
+
void pt_tracee_initialize(pt_tracee_t *tracee);
|
14
|
+
pt_tracee_t *pt_tracee_add(pt_tracee_t **headpp, pid_t pid);
|
15
|
+
pt_tracee_t *pt_tracee_find_or_add(pt_tracee_t **headpp, pid_t pid);
|
16
|
+
int pt_tracee_wipedoutq(pt_tracee_t *headp);
|
17
|
+
void pt_tracee_free(pt_tracee_t **headpp);
|
18
|
+
int pt_tracee_length(pt_tracee_t *tracee);
|
19
|
+
|
20
|
+
#endif
|
data/lib/process_tail.rb
CHANGED
@@ -1,87 +1,107 @@
|
|
1
|
-
require 'thread'
|
2
1
|
require 'process_tail/process_tail'
|
3
2
|
require 'process_tail/version'
|
3
|
+
require 'erb'
|
4
|
+
require 'thread'
|
4
5
|
|
5
6
|
module ProcessTail
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
def
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
}
|
7
|
+
module ReadIOExtension
|
8
|
+
attr_accessor :process_tail_after_close
|
9
|
+
|
10
|
+
def close
|
11
|
+
super
|
12
|
+
ensure
|
13
|
+
if hook = process_tail_after_close
|
14
|
+
hook.call
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
18
|
|
19
|
-
|
19
|
+
class Tracer
|
20
|
+
def attach
|
21
|
+
case
|
22
|
+
when respond_to?(:ptrace_attach, true)
|
23
|
+
ptrace_attach
|
24
|
+
when respond_to?(:dtrace_attach, true)
|
25
|
+
dtrace_attach
|
26
|
+
else
|
27
|
+
raise NotImplementedError
|
28
|
+
end
|
20
29
|
|
21
|
-
|
22
|
-
|
30
|
+
self
|
31
|
+
end
|
23
32
|
|
24
|
-
|
25
|
-
|
33
|
+
def wait
|
34
|
+
trace_thread.join
|
26
35
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
36
|
+
self
|
37
|
+
rescue StopTracing
|
38
|
+
# NOP
|
31
39
|
end
|
32
40
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
|
41
|
+
def detach
|
42
|
+
trace_thread.kill if trace_thread.alive?
|
43
|
+
|
44
|
+
wait
|
37
45
|
end
|
38
46
|
|
39
47
|
private
|
40
48
|
|
41
|
-
def
|
42
|
-
|
43
|
-
wait_queue = Queue.new
|
44
|
-
trace_thread = Thread.fork {
|
45
|
-
begin
|
46
|
-
task_ids.each do |tid|
|
47
|
-
attach tid
|
48
|
-
end
|
49
|
-
|
50
|
-
do_trace extract_fd(fd), wait_queue, &block
|
51
|
-
ensure
|
52
|
-
task_ids.each do |tid|
|
53
|
-
detach tid
|
54
|
-
end
|
55
|
-
end
|
56
|
-
}
|
49
|
+
def generate_dscript
|
50
|
+
template = File.read(File.join(__dir__, 'process_tail/process_tail.d'))
|
57
51
|
|
58
|
-
|
52
|
+
ERB.new(template).result(binding)
|
53
|
+
end
|
54
|
+
end
|
59
55
|
|
60
|
-
|
56
|
+
class << self
|
57
|
+
def each(pid)
|
58
|
+
tracer = trace(pid, :all) {|tid, fd, str|
|
59
|
+
yield tid, fd, str
|
60
|
+
}
|
61
|
+
|
62
|
+
tracer.wait
|
63
|
+
rescue StopTracing
|
64
|
+
# NOP
|
65
|
+
ensure
|
66
|
+
tracer.detach
|
61
67
|
end
|
62
68
|
|
63
|
-
def
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
69
|
+
def open(pid, fd = :stdout)
|
70
|
+
read_io, write_io = IO.pipe
|
71
|
+
|
72
|
+
tracer = trace(pid, fd) {|tid, fd_num, str|
|
73
|
+
unless read_io.closed?
|
74
|
+
write_io.write str
|
75
|
+
else
|
76
|
+
write_io.close
|
77
|
+
process_tail.detach
|
71
78
|
end
|
72
79
|
}
|
73
|
-
end
|
74
80
|
|
75
|
-
|
76
|
-
|
77
|
-
|
81
|
+
read_io.extend ReadIOExtension
|
82
|
+
read_io.process_tail_after_close = -> {
|
83
|
+
write_io.close unless write_io.closed?
|
84
|
+
tracer.detach
|
85
|
+
}
|
86
|
+
|
87
|
+
block_given? ? yield(read_io) : read_io
|
88
|
+
rescue StopTracing
|
89
|
+
# NOP
|
90
|
+
ensure
|
91
|
+
if block_given?
|
92
|
+
[read_io, write_io].each do |io|
|
93
|
+
io.close unless io.closed?
|
94
|
+
end
|
78
95
|
end
|
79
96
|
end
|
80
97
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
98
|
+
private
|
99
|
+
|
100
|
+
def trace(pid, fd, &block)
|
101
|
+
pt = Tracer.new(pid, extract_fd(fd), &block)
|
102
|
+
pt.attach
|
103
|
+
|
104
|
+
pt
|
85
105
|
end
|
86
106
|
|
87
107
|
def extract_fd(name)
|
@@ -0,0 +1,77 @@
|
|
1
|
+
inline int PID = <%= pid %>;
|
2
|
+
inline int FD = <%= fd %>;
|
3
|
+
|
4
|
+
dtrace:::BEGIN
|
5
|
+
{
|
6
|
+
processes[pid] = 0;
|
7
|
+
self->child = 0;
|
8
|
+
}
|
9
|
+
|
10
|
+
syscall:::entry
|
11
|
+
/processes[ppid] == -1 && self->child == 0/
|
12
|
+
{
|
13
|
+
self->child = 1;
|
14
|
+
}
|
15
|
+
|
16
|
+
syscall:::entry
|
17
|
+
/processes[ppid] > 0 && self->child == 0/
|
18
|
+
{
|
19
|
+
this->vforking_tid = processes[ppid];
|
20
|
+
self->child = (this->vforking_tid == tid) ? 0 : 1;
|
21
|
+
}
|
22
|
+
|
23
|
+
syscall:::entry
|
24
|
+
/pid == PID || self->child/
|
25
|
+
{
|
26
|
+
self->tracked = 1;
|
27
|
+
self->arg0 = arg0;
|
28
|
+
self->arg1 = arg1;
|
29
|
+
self->arg2 = arg2;
|
30
|
+
}
|
31
|
+
|
32
|
+
syscall::fork:entry
|
33
|
+
/self->tracked/
|
34
|
+
{
|
35
|
+
processes[pid] = -1;
|
36
|
+
}
|
37
|
+
|
38
|
+
syscall::vfork:entry
|
39
|
+
/self->tracked/
|
40
|
+
{
|
41
|
+
processes[pid] = tid;
|
42
|
+
}
|
43
|
+
|
44
|
+
syscall::exit:entry
|
45
|
+
{
|
46
|
+
self->child = 0;
|
47
|
+
processes[pid] = 0;
|
48
|
+
}
|
49
|
+
|
50
|
+
syscall::exit:entry
|
51
|
+
/pid == PID/
|
52
|
+
{
|
53
|
+
exit(0);
|
54
|
+
}
|
55
|
+
|
56
|
+
syscall:::entry
|
57
|
+
/!((probefunc == "write" || probefunc == "write_nocancel") && (FD == 0 || self->arg0 == FD))/
|
58
|
+
{
|
59
|
+
self->tracked = 0;
|
60
|
+
self->arg0 = 0;
|
61
|
+
self->arg1 = 0;
|
62
|
+
self->arg2 = 0;
|
63
|
+
}
|
64
|
+
|
65
|
+
syscall::write:return,
|
66
|
+
syscall::write_nocancel:return
|
67
|
+
/self->tracked/
|
68
|
+
{
|
69
|
+
trace(pid); /* pid */
|
70
|
+
trace(self->arg0); /* fd */
|
71
|
+
trace(stringof(copyin(self->arg1, arg0))); /* content */
|
72
|
+
|
73
|
+
self->tracked = 0;
|
74
|
+
self->arg0 = 0;
|
75
|
+
self->arg1 = 0;
|
76
|
+
self->arg2 = 0;
|
77
|
+
}
|
data/lib/process_tail/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: process_tail
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hika Hibariya
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-02-
|
11
|
+
date: 2015-02-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -84,7 +84,15 @@ files:
|
|
84
84
|
- bin/process_tail
|
85
85
|
- ext/process_tail/extconf.rb
|
86
86
|
- ext/process_tail/process_tail.c
|
87
|
+
- ext/process_tail/process_tail.h
|
88
|
+
- ext/process_tail/pt_dtrace.c
|
89
|
+
- ext/process_tail/pt_dtrace.h
|
90
|
+
- ext/process_tail/pt_ptrace.c
|
91
|
+
- ext/process_tail/pt_ptrace.h
|
92
|
+
- ext/process_tail/pt_tracee.c
|
93
|
+
- ext/process_tail/pt_tracee.h
|
87
94
|
- lib/process_tail.rb
|
95
|
+
- lib/process_tail/process_tail.d
|
88
96
|
- lib/process_tail/version.rb
|
89
97
|
- process_tail.gemspec
|
90
98
|
- spec/process_tail_spec.rb
|