process_tail 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 85d86bdf54735a348134ab9abceec3610882fe23
4
- data.tar.gz: 9f1e65346bd355e4fb1b9f9c83fdf8c45a16a224
3
+ metadata.gz: 3adc23055f8977b049a04d0b94ddb2f9a5740cf2
4
+ data.tar.gz: 680424fb0373ed7d1e429044920ced1033151278
5
5
  SHA512:
6
- metadata.gz: e3da11ae5cece8697449bcea8307bc34e1af5b6cb3a39834e96d56f2b32d6306e4ac9070909c79bab0f2486f319a15de408d13df6982d9d3fa367163920c8387
7
- data.tar.gz: 30b6556d39cf4b06169e518a69fe7a8c1b0b03a05e0f0db97bcd6abafee42625622048885fbc7452dfbf48cec4915983c2feb10aa6aae5ea132a7a7b8c770b56
6
+ metadata.gz: 90c7886af47819ac7ae13281bec9a157cb051569f1799dfa11d7edaa23ecdc1e3a1298febbb59ef6202fba679a32e28dbf87ecd34e0c23623c522406da51d846
7
+ data.tar.gz: ed2d69a06283896f786f36aa5a960ec9fc1e1b9d059840f15686c49a88d0611eb140e256b81599c0e66669122f2f579445c55015d57613e445f2add89594cd71
data/README.md CHANGED
@@ -11,7 +11,6 @@ So you can get other process outputs.
11
11
 
12
12
  * Mac OSX and Windows are not supported at the moment
13
13
  * SEGV occures occasionally
14
- * Multithreaded process support
15
14
 
16
15
  ## Installation
17
16
 
@@ -31,12 +30,20 @@ Or install it yourself as:
31
30
 
32
31
  ## Usage
33
32
 
33
+ ### Get outputs as an IO object
34
+
34
35
  ```ruby
35
36
  ProcessTail.open pid, :stdout do |io|
36
37
  puts "Recent stdout of #{pid}: #{io.gets}"
37
38
  end
38
39
  ```
39
40
 
41
+ ### Show outputs instantly
42
+
43
+ ```bash
44
+ $ process_tail $(pgrep PROCESS_NAME)
45
+ ```
46
+
40
47
  ## Contributing
41
48
 
42
49
  1. Fork it ( https://github.com/hibariya/process_tail/fork )
data/bin/process_tail CHANGED
@@ -5,7 +5,11 @@ require 'process_tail'
5
5
  $stdout.sync = true
6
6
 
7
7
  ProcessTail.open Integer(ARGV[0]) do |io|
8
- while c = io.getc
9
- print c
8
+ begin
9
+ while c = io.getc
10
+ print c
11
+ end
12
+ rescue IOError
13
+ # NOP
10
14
  end
11
15
  end
@@ -8,7 +8,10 @@ end
8
8
  abort "missing #{m}" unless have_struct_member('struct user_regs_struct', m, 'sys/user.h')
9
9
  end
10
10
 
11
- abort 'missing __WALL' unless have_const('__WALL', 'sys/wait.h')
12
- abort 'missing PTRACE_O_TRACESYSGOOD' unless have_const('PTRACE_O_TRACESYSGOOD', 'sys/ptrace.h')
11
+ %w(PTRACE_O_TRACESYSGOOD PTRACE_O_TRACEFORK PTRACE_O_TRACEVFORK PTRACE_O_TRACECLONE).each do |c|
12
+ abort "missing #{c}" unless have_const(c, 'sys/ptrace.h')
13
+ end
14
+
15
+ abort 'missing __WALL' unless have_const('__WALL', 'sys/wait.h')
13
16
 
14
17
  create_makefile 'process_tail/process_tail'
@@ -15,11 +15,26 @@ typedef struct pt_tracee {
15
15
  struct pt_tracee *next;
16
16
  } pt_tracee_t;
17
17
 
18
- typedef struct pt_wait_args {
18
+ typedef struct {
19
19
  int *status;
20
20
  pid_t pid;
21
21
  } pt_wait_args_t;
22
22
 
23
+ typedef struct {
24
+ unsigned int fd;
25
+ pt_tracee_t *tracee;
26
+ VALUE *wait_queue;
27
+ } pt_loop_args_t;
28
+
29
+ static void
30
+ pt_tracee_initialize(pt_tracee_t *tracee)
31
+ {
32
+ tracee->pid = 0;
33
+ tracee->activated = 0;
34
+ tracee->dead = 0;
35
+ tracee->next = NULL;
36
+ }
37
+
23
38
  static pt_tracee_t *
24
39
  pt_tracee_add(pt_tracee_t **headpp, pid_t pid)
25
40
  {
@@ -29,10 +44,9 @@ pt_tracee_add(pt_tracee_t **headpp, pid_t pid)
29
44
  exit(EXIT_FAILURE);
30
45
  }
31
46
 
32
- tracee->pid = pid;
33
- tracee->activated = 0;
34
- tracee->dead = 0;
35
- tracee->next = (struct pt_tracee *)*headpp;
47
+ pt_tracee_initialize(tracee);
48
+ tracee->pid = pid;
49
+ tracee->next = (struct pt_tracee *)*headpp;
36
50
 
37
51
  (*headpp) = tracee;
38
52
 
@@ -42,14 +56,15 @@ pt_tracee_add(pt_tracee_t **headpp, pid_t pid)
42
56
  static pt_tracee_t *
43
57
  pt_tracee_find_or_add(pt_tracee_t **headpp, pid_t pid)
44
58
  {
45
- pt_tracee_t *tracee = *headpp;
59
+ pt_tracee_t *headp = *headpp;
60
+ pt_tracee_t *current = headp;
46
61
 
47
- while (tracee != NULL) {
48
- if (tracee->pid == pid) {
49
- return tracee;
62
+ while (current) {
63
+ if (current->pid == pid) {
64
+ return current;
50
65
  }
51
66
 
52
- tracee = tracee->next;
67
+ current = current->next;
53
68
  }
54
69
 
55
70
  return pt_tracee_add(headpp, pid);
@@ -58,14 +73,14 @@ pt_tracee_find_or_add(pt_tracee_t **headpp, pid_t pid)
58
73
  static int
59
74
  pt_tracee_wipedoutq(pt_tracee_t *headp)
60
75
  {
61
- pt_tracee_t *tracee = headp;
76
+ pt_tracee_t *current = headp;
62
77
 
63
- while (tracee != NULL) {
64
- if (tracee->dead == 0) {
78
+ while (current) {
79
+ if (current->dead == 0) {
65
80
  return 0;
66
81
  }
67
82
 
68
- tracee = tracee->next;
83
+ current = current->next;
69
84
  }
70
85
 
71
86
  return 1;
@@ -74,20 +89,14 @@ pt_tracee_wipedoutq(pt_tracee_t *headp)
74
89
  static void
75
90
  pt_tracee_free(pt_tracee_t **headpp)
76
91
  {
77
- pt_tracee_t *tracee = *headpp;
92
+ pt_tracee_t *tracee = *headpp;
78
93
 
79
- if (tracee->next) {
94
+ if (tracee) {
80
95
  pt_tracee_free(&tracee->next);
81
- }
82
-
83
- free(tracee);
84
- (*headpp) = NULL;
85
- }
86
96
 
87
- static int
88
- pt_io_closedq(VALUE io)
89
- {
90
- return rb_funcall(io, rb_intern("closed?"), 0) == Qtrue;
97
+ free(tracee);
98
+ tracee = NULL;
99
+ }
91
100
  }
92
101
 
93
102
  static void *
@@ -101,7 +110,7 @@ pt_wait(void *argp)
101
110
  }
102
111
 
103
112
  static void
104
- pt_get_data(pid_t pid, long addr, char *string, int length)
113
+ pt_read_data(pid_t pid, long addr, char *string, int length)
105
114
  {
106
115
  int i;
107
116
  long data;
@@ -123,39 +132,33 @@ pt_ptrace_syscall(pid_t pid, long signal)
123
132
  rb_thread_schedule();
124
133
  }
125
134
 
126
- static void
127
- pt_loop(unsigned int fd, VALUE write_io, VALUE read_io, VALUE wait_queue, pt_tracee_t *tracee_headp)
135
+ static VALUE
136
+ pt_loop(VALUE loop_args)
128
137
  {
138
+ pt_loop_args_t *args = (void *)loop_args;
139
+
129
140
  char *string = NULL;
130
141
  int syscall = 0;
131
142
  int status;
132
- long signal;
133
143
  pid_t pid;
134
144
  pt_tracee_t *tracee;
135
145
  pt_wait_args_t pt_wait_args = {&status};
136
146
 
137
- // NOTE: system call: eax, arguments: rdi, rsi, rdx, r10, r8, r9
147
+ // NOTE: system call: orig_rax, arguments: rdi, rsi, rdx, r10, r8, r9
138
148
  struct user_regs_struct regs;
139
149
 
140
150
  while (1) {
141
151
  rb_thread_call_without_gvl(pt_wait, &pt_wait_args, RUBY_UBF_PROCESS, NULL);
142
152
 
143
- signal = 0;
144
153
  pid = pt_wait_args.pid;
145
- tracee = pt_tracee_find_or_add(&tracee_headp, pid);
146
-
147
- if (pt_io_closedq(read_io)) {
148
- pt_tracee_free(&tracee_headp);
149
-
150
- return;
151
- }
154
+ tracee = pt_tracee_find_or_add(&args->tracee, pid);
152
155
 
153
156
  if (tracee->activated == 0) {
154
- ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACESYSGOOD);
155
157
  tracee->activated = 1;
158
+ rb_funcall(*args->wait_queue, rb_intern("enq"), 1, INT2FIX((int)pid));
156
159
 
157
- rb_funcall(wait_queue, rb_intern("enq"), 1, INT2FIX((int)pid));
158
- pt_ptrace_syscall(pid, signal);
160
+ ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE);
161
+ pt_ptrace_syscall(pid, 0);
159
162
 
160
163
  continue;
161
164
  }
@@ -163,18 +166,15 @@ pt_loop(unsigned int fd, VALUE write_io, VALUE read_io, VALUE wait_queue, pt_tra
163
166
  if (WIFEXITED(status) || WIFSIGNALED(status)) {
164
167
  tracee->dead = 1;
165
168
 
166
- if (pt_tracee_wipedoutq(tracee_headp)) {
167
- pt_tracee_free(&tracee_headp);
168
-
169
- return;
169
+ if (pt_tracee_wipedoutq(args->tracee)) {
170
+ return Qnil;
170
171
  }
171
172
 
172
173
  continue;
173
174
  }
174
175
 
175
176
  if (!(WIFSTOPPED(status) && WSTOPSIG(status) & 0x80)) {
176
- signal = WSTOPSIG(status);
177
- pt_ptrace_syscall(pid, signal);
177
+ pt_ptrace_syscall(pid, WSTOPSIG(status));
178
178
 
179
179
  continue;
180
180
  }
@@ -182,35 +182,30 @@ pt_loop(unsigned int fd, VALUE write_io, VALUE read_io, VALUE wait_queue, pt_tra
182
182
  ptrace(PTRACE_GETREGS, pid, 0, &regs);
183
183
 
184
184
  if (regs.orig_rax != SYS_write) {
185
- pt_ptrace_syscall(pid, signal);
185
+ pt_ptrace_syscall(pid, 0);
186
186
 
187
187
  continue;
188
188
  }
189
189
 
190
190
  syscall = 1 - syscall;
191
- if (syscall && (fd == 0 || regs.rdi == fd)) {
191
+ if (syscall && (args->fd == 0 || regs.rdi == args->fd)) {
192
192
  string = malloc(regs.rdx + sizeof(long));
193
193
 
194
194
  if (string == NULL) {
195
195
  exit(EXIT_FAILURE);
196
196
  }
197
197
 
198
- pt_get_data(pid, regs.rsi, string, regs.rdx);
199
-
200
- if (pt_io_closedq(read_io)) {
201
- free(string);
202
- pt_tracee_free(&tracee_headp);
203
-
204
- return;
205
- }
198
+ pt_read_data(pid, regs.rsi, string, regs.rdx);
206
199
 
207
- rb_io_write(write_io, rb_str_new_cstr(string));
200
+ rb_yield(rb_ary_new_from_args(3, rb_str_new_cstr(string), INT2FIX((int)pid), INT2FIX(args->fd)));
208
201
 
209
202
  free(string);
210
203
  }
211
204
 
212
- pt_ptrace_syscall(pid, signal);
205
+ pt_ptrace_syscall(pid, 0);
213
206
  }
207
+
208
+ return Qnil;
214
209
  }
215
210
 
216
211
  static VALUE
@@ -240,15 +235,24 @@ pt_detach(VALUE klass, VALUE pidv)
240
235
  }
241
236
 
242
237
  static VALUE
243
- pt_trace(VALUE klass, VALUE fdp, VALUE write_io, VALUE read_io, VALUE wait_queue)
238
+ pt_finalize(VALUE traceev)
239
+ {
240
+ pt_tracee_t *tracee = (void *)traceev;
241
+
242
+ pt_tracee_free(&tracee);
243
+
244
+ return Qnil;
245
+ }
246
+
247
+ static VALUE
248
+ pt_trace(VALUE klass, VALUE fdv, VALUE wait_queue)
244
249
  {
245
- pt_tracee_t *tracee_headp = NULL;
250
+ pt_tracee_t *tracee = NULL;
251
+ pt_loop_args_t loop_args = {FIX2INT(fdv), tracee, &wait_queue};
246
252
 
247
- Check_Type(fdp, T_FIXNUM);
248
- Check_Type(write_io, T_FILE);
249
- Check_Type(read_io, T_FILE);
253
+ Check_Type(fdv, T_FIXNUM);
250
254
 
251
- pt_loop((unsigned int)FIX2INT(fdp), write_io, read_io, wait_queue, tracee_headp);
255
+ rb_ensure(pt_loop, (VALUE)&loop_args, pt_finalize, (VALUE)tracee);
252
256
 
253
257
  return Qnil;
254
258
  }
@@ -258,7 +262,7 @@ Init_process_tail()
258
262
  {
259
263
  VALUE ProcessTail = rb_define_module("ProcessTail");
260
264
 
261
- rb_define_singleton_method(ProcessTail, "attach", pt_attach, 1);
262
- rb_define_singleton_method(ProcessTail, "do_trace", pt_trace, 4);
263
- rb_define_singleton_method(ProcessTail, "detach", pt_detach, 1);
265
+ rb_define_private_method(rb_singleton_class(ProcessTail), "attach", pt_attach, 1);
266
+ rb_define_private_method(rb_singleton_class(ProcessTail), "do_trace", pt_trace, 2);
267
+ rb_define_private_method(rb_singleton_class(ProcessTail), "detach", pt_detach, 1);
264
268
  }
@@ -1,3 +1,3 @@
1
1
  module ProcessTail
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.3'
3
3
  end
data/lib/process_tail.rb CHANGED
@@ -7,50 +7,69 @@ module ProcessTail
7
7
 
8
8
  class << self
9
9
  def open(pid, fd = :stdout)
10
- io = trace(pid, fd)
10
+ read_io, write_io = IO.pipe
11
+ trace_thread = trace(pid, fd) {|str, *|
12
+ unless read_io.closed?
13
+ write_io.write str
14
+ else
15
+ trace_thread.kill
16
+ end
17
+ }
18
+
19
+ wait_thread = fork_wait_thread(trace_thread, [read_io, write_io])
20
+
21
+ if block_given?
22
+ value = yield(read_io)
23
+
24
+ trace_thread.kill
25
+ wait_thread.join
11
26
 
12
- block_given? ? yield(io) : io
13
- ensure
14
- io.close if block_given? && !io.closed?
27
+ value
28
+ else
29
+ read_io
30
+ end
15
31
  end
16
32
 
17
- def trace(pid, fd = :stdout)
33
+ def trace(pid, fd = :stdout, &block)
18
34
  TRACE_LOCK.synchronize {
19
- trace_without_lock(pid, fd)
35
+ trace_without_lock(pid, fd, &block)
20
36
  }
21
37
  end
22
38
 
23
39
  private
24
40
 
25
- def trace_without_lock(pid, fd)
26
- read_io, write_io = IO.pipe
27
- task_ids = extract_task_ids(pid)
28
- wait_queue = Queue.new
29
-
30
- thread = Thread.fork {
41
+ def trace_without_lock(pid, fd, &block)
42
+ task_ids = extract_task_ids(pid)
43
+ wait_queue = Queue.new
44
+ trace_thread = Thread.fork {
31
45
  begin
32
- extract_task_ids(pid).each do |tid|
46
+ task_ids.each do |tid|
33
47
  attach tid
34
48
  end
35
49
 
36
- do_trace extract_fd(fd), write_io, read_io, wait_queue
50
+ do_trace extract_fd(fd), wait_queue, &block
37
51
  ensure
38
- write_io.close unless write_io.closed?
39
-
40
52
  task_ids.each do |tid|
41
53
  detach tid
42
54
  end
43
55
  end
44
56
  }
45
57
 
46
- at_exit do
47
- thread.kill
48
- thread.join
49
- end
50
-
51
58
  wait_all_attach task_ids, wait_queue
52
59
 
53
- read_io
60
+ trace_thread
61
+ end
62
+
63
+ def fork_wait_thread(trace_thread, pipe)
64
+ Thread.fork {
65
+ begin
66
+ trace_thread.join
67
+ ensure
68
+ pipe.each do |io|
69
+ io.close unless io.closed?
70
+ end
71
+ end
72
+ }
54
73
  end
55
74
 
56
75
  def wait_all_attach(task_ids, waitq)
@@ -21,15 +21,14 @@ describe ProcessTail do
21
21
  def expect_same_output_and_no_error(fd, method, string)
22
22
  greeting = process_maker { send(method, string) }
23
23
  pid = greeting.call
24
- io = ProcessTail.trace(pid, fd)
25
24
 
26
- Process.kill :USR1, pid # resume child
25
+ ProcessTail.open pid, fd do |io|
26
+ Process.kill :USR1, pid # resume child
27
27
 
28
- "#{string}\n".each_line do |expected|
29
- expect(io.gets).to eq expected
28
+ "#{string}\n".each_line do |expected|
29
+ expect(io.gets).to eq expected
30
+ end
30
31
  end
31
-
32
- io.close
33
32
  end
34
33
 
35
34
  around do |example|
@@ -40,11 +39,12 @@ describe ProcessTail do
40
39
  timeout 5 do
41
40
  example.run
42
41
  end
43
-
44
42
  ensure
45
43
  $recorded_children.each do |child|
46
44
  Process.kill :TERM, child
47
45
  end
46
+
47
+ sleep 1 # FIXME :(
48
48
  end
49
49
  end
50
50
 
@@ -71,9 +71,7 @@ describe ProcessTail do
71
71
 
72
72
  expect(read_io).to be_closed
73
73
  end
74
- end
75
74
 
76
- describe '.trace' do
77
75
  specify 'simple stdout' do
78
76
  expect_same_output_and_no_error :stdout, :puts, 'HELLO'
79
77
  end
@@ -82,6 +80,10 @@ describe ProcessTail do
82
80
  expect_same_output_and_no_error :stderr, :warn, 'HELLO'
83
81
  end
84
82
 
83
+ specify 'multiline' do
84
+ expect_same_output_and_no_error :stdout, :puts, "HELLO\nHELLO"
85
+ end
86
+
85
87
  specify 'multithreaded' do
86
88
  pid = Process.fork {
87
89
  trap :USR1 do
@@ -90,7 +92,7 @@ describe ProcessTail do
90
92
 
91
93
  Thread.fork do
92
94
  trap :USR2 do
93
- puts 'HELLO'
95
+ puts 'LOVE AND KISSES'
94
96
  end
95
97
 
96
98
  sleep
@@ -100,19 +102,15 @@ describe ProcessTail do
100
102
  }
101
103
 
102
104
  $recorded_children << pid
103
- io = ProcessTail.trace(pid, :stdout)
105
+ io = ProcessTail.open(pid, :stdout)
104
106
 
105
107
  Process.kill :USR1, pid
106
108
  Process.kill :USR2, pid
107
109
 
108
110
  expect(io.gets).to eq "HELLO\n"
109
- expect(io.gets).to eq "HELLO\n"
111
+ expect(io.gets).to eq "LOVE AND KISSES\n"
110
112
 
111
113
  io.close
112
114
  end
113
-
114
- specify 'multiline' do
115
- expect_same_output_and_no_error :stdout, :puts, "HELLO\nLOVE AND KISSES"
116
- end
117
115
  end
118
116
  end
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.2
4
+ version: 0.0.3
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-08 00:00:00.000000000 Z
11
+ date: 2015-02-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler