ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Coro/Coro/Debug.pm
Revision: 1.14
Committed: Sun Sep 23 21:49:58 2007 UTC (16 years, 8 months ago) by root
Branch: MAIN
Changes since 1.13: +4 -6 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 =head1 NAME
2
3 Coro::Debug - various functions that help debugging Coro programs
4
5 =head1 SYNOPSIS
6
7 use Coro::Debug;
8
9 our $server = new_server Coro::Debug path => "/tmp/socketpath";
10
11 $ socat readline: unix:/tmp/socketpath
12
13 =head1 DESCRIPTION
14
15 This module provides some debugging facilities. Most will, if not handled
16 carefully, severely compromise the security of your program, so use it
17 only for debugging (or take other precautions).
18
19 It mainly implements a very primitive debugger that lets you list running
20 coroutines:
21
22 > ps
23 pid RUND RSS description where
24 43383424 ---- 10 [async_pool idle] [/opt/perl/lib/perl5/Coro.pm:256]
25 46127008 ---- 5 worldmap updater [/opt/cf/ext/item-worldmap.ext:116]
26 18334288 ---- 4 music scheduler [/opt/cf/ext/player-env.ext:77]
27 24559856 ---- 14 [async_pool idle] [/opt/perl/lib/perl5/Coro.pm:256]
28 20170640 ---- 6 map scheduler [/opt/cf/ext/map-scheduler.ext:62]
29 18492336 ---- 5 player scheduler [/opt/cf/ext/login.ext:501]
30 15607952 ---- 2 timeslot manager [/opt/cf/cf.pm:382]
31 11015408 ---- 2 [unblock_sub schedul [/opt/perl/lib/perl5/Coro.pm:548]
32 11015088 ---- 2 [coro manager] [/opt/perl/lib/perl5/Coro.pm:170]
33 11014896 -U-- 835 [main::] [/opt/cf/ext/dm-support.ext:45]
34
35 Lets you do backtraces on about any coroutine:
36
37 > bt 18334288
38 coroutine is at /opt/cf/ext/player-env.ext line 77
39 eval {...} called at /opt/cf/ext/player-env.ext line 77
40 ext::player_env::__ANON__ called at -e line 0
41 Coro::_run_coro called at -e line 0
42
43 Or lets you eval perl code:
44
45 > p 5+7
46 12
47
48 Or lets you eval perl code within other coroutines:
49
50 > eval 18334288 $_
51 1
52
53 =over 4
54
55 =cut
56
57 package Coro::Debug;
58
59 use strict;
60
61 use Carp ();
62 use IO::Socket::UNIX;
63 use AnyEvent;
64
65 use Coro ();
66 use Coro::Handle ();
67 use Coro::State ();
68
69 sub find_coro {
70 my ($pid) = @_;
71 if (my ($coro) = grep $_ == $1, Coro::State::list) {
72 $coro
73 } else {
74 print "$pid: no such coroutine\n";
75 undef
76 }
77 }
78
79 =item command $string
80
81 Execute a debugger command, sending any output to STDOUT. Used by
82 C<session>, below.
83
84 =cut
85
86 sub command($) {
87 my ($cmd) = @_;
88
89 $cmd =~ s/[\012\015]$//;
90
91 if ($cmd =~ /^ps/) {
92 printf "%20s %s%s %4s %-20.20s %s\n", "pid", "S", "S", "RSS", "description", "where";
93 for my $coro (Coro::State::list) {
94 Coro::cede;
95 my @bt;
96 $coro->_eval (sub {
97 # we try to find *the* definite frame that gives msot useful info
98 # by skipping Coro frames and pseudo-frames.
99 for my $frame (1..10) {
100 my @frame = caller $frame;
101 @bt = @frame if $frame[2];
102 last unless $bt[0] =~ /^Coro/;
103 }
104 });
105 printf "%20s %s%s %4d %-20.20s %s\n",
106 $coro+0,
107 $coro->is_new ? "N" : $coro->is_running ? "U" : $coro->is_ready ? "R" : "-",
108 $coro->has_stack ? "S" : "-",
109 $coro->rss / 1024,
110 $coro->debug_desc,
111 (@bt ? sprintf "[%s:%d]", $bt[1], $bt[2] : "-");
112 }
113
114 } elsif ($cmd =~ /bt\s+(\d+)/) {
115 if (my $coro = find_coro $1) {
116 my $bt;
117 $coro->_eval (sub { $bt = Carp::longmess "coroutine is" });
118 if ($bt) {
119 print $bt;
120 } else {
121 print "$1: unable to get backtrace\n";
122 }
123 }
124
125 } elsif ($cmd =~ /p\s+(.*)$/) {
126 my @res = eval $1;
127 print $@ ? $@ : (join " ", @res) . "\n";
128
129 } elsif ($cmd =~ /eval\s+(\d+)\s+(.*)$/) {
130 if (my $coro = find_coro $1) {
131 my $cmd = $2;
132 my @res;
133 $coro->_eval (sub { my @res = eval $cmd });
134 print $@ ? $@ : (join " ", @res, "\n");
135 }
136
137 } elsif ($cmd =~ /^help/) {
138 print <<EOF;
139 ps show the list of all coroutines
140 bt <pid> show a full backtrace of coroutine <pid>
141 p <perl> evaluate <perl> expression and print results
142 eval <pid> <perl> evaluate <perl> expression in context of <pid> (dangerous!)
143 exit end this session
144
145 EOF
146
147 } else {
148 print "$cmd: unknown command\n";
149 }
150 }
151
152 =item session $fh
153
154 Run an interactive debugger session on the given filehandle. Each line entered
155 is simply passed to C<command>
156
157 =cut
158
159 sub session($) {
160 my ($fh) = @_;
161
162 $fh = Coro::Handle::unblock $fh;
163 select $fh;
164
165 print "coro debug session. use help for more info\n\n";
166
167 while ((print "> "), defined (my $cmd = $fh->readline ("\012"))) {
168 if ($cmd =~ /^exit/) {
169 print "bye.\n";
170 last;
171 }
172
173 command $cmd;
174 }
175 }
176
177 =item $server = new_unix_server Coro::Debug $path
178
179 Creates a new unix domain socket that listens for connection requests and
180 runs C<session> on any connection. Normal unix permission checks and umask
181 applies, so you can protect your socket by puttint it into a protected
182 directory.
183
184 The C<socat> utility is an excellent way to connect to this socket,
185 offering readline and history support:
186
187 socat readline:history=/tmp/hist.corodebug unix:/path/to/socket
188
189 The server accepts connections until it is destroyed, so you should keep
190 the return value around as long as you want the server to stay available.
191
192 =cut
193
194 sub new_unix_server {
195 my ($class, $path) = @_;
196
197 unlink $path;
198 my $fh = new IO::Socket::UNIX Listen => 1, Local => $path
199 or Carp::croak "Coro::Debug::Server($path): $!";
200
201 my $self = bless {
202 fh => $fh,
203 path => $path,
204 }, $class;
205
206 $self->{cw} = AnyEvent->io (fh => $fh, poll => 'r', cb => sub {
207 Coro::async_pool {
208 $Coro::current->desc ("[Coro::Debug session]");
209 my $fh = $fh->accept;
210 session $fh;
211 close $fh;
212 };
213 });
214
215 $self
216 }
217
218 sub DESTROY {
219 my ($self) = @_;
220
221 unlink $self->{path};
222 close $self->{fh};
223 %$self = ();
224 }
225
226 1;
227
228 =back
229
230 =head1 AUTHOR
231
232 Marc Lehmann <schmorp@schmorp.de>
233 http://home.schmorp.de/
234
235 =cut
236
237