ucode: check for errors in ftruncate()
[project/udebug.git] / udebug-cli
1 #!/usr/bin/env ucode
2 'use strict';
3 import { basename } from "fs";
4 let udebug = require("udebug");
5 let uloop = require("uloop");
6 let libubus = require("ubus");
7 uloop.init();
8 let ubus = libubus.connect();
9
10 let opts = {
11 select: []
12 };
13
14 const usage_message = `
15 Usage: ${basename(sourcepath())} [<options>] <command> [<args>]
16
17 Options:
18 -f Ignore errors on opening rings
19 -d <duration>: Only fetch data up to <duration> seconds old
20 -o <file>|- Set output file for snapshot/stream (or '-' for stdout)
21 -i <process>[:<name>] Select debug buffer for snapshot/stream
22 -s <path> Use udebug socket <path>
23 -q Suppress warnings/error messages
24
25 Commands:
26 list: List available debug buffers
27 snapshot: Create a pcapng snapshot of debug buffers
28 set_flag [<name>=0|1 ...] Set ring buffer flags
29 get_flags Get ring buffer flags
30
31 `;
32
33 function _warn(str) {
34 if (opts.quiet)
35 return;
36
37 warn(str);
38 }
39
40 function usage() {
41 warn(usage_message);
42 exit(1);
43 }
44
45 while (substr(ARGV[0], 0, 1) == "-") {
46 let opt = substr(shift(ARGV), 1);
47 switch(opt) {
48 case 'd':
49 opts.duration = +shift(ARGV);
50 break;
51 case 's':
52 opts.socket = shift(ARGV);
53 break;
54 case 'i':
55 push(opts.select, shift(ARGV));
56 break;
57 case 'o':
58 opts.output_file = shift(ARGV);
59 break;
60 case 'q':
61 opts.quiet = true;
62 break;
63 case 'f':
64 opts.force = true;
65 break;
66 default:
67 usage();
68 }
69 }
70
71 let procs = {};
72 let selected = [];
73 let rings = {};
74 let subscriber;
75 let pcap;
76
77 function ring_selected(ring) {
78 if (!length(opts.select))
79 return true;
80
81 for (let sel in opts.select) {
82 let match = split(sel, ":", 2);
83 if (wildcard(ring.proc_name, match[0]) &&
84 (!match[1] || wildcard(ring.ring_name, match[1])))
85 return true;
86 }
87
88 return false;
89 }
90
91 function poll_data() {
92 let data = [];
93 for (let ring_id in rings) {
94 let ring = rings[ring_id];
95 let s = ring[1].fetch();
96 if (s)
97 push(data, s);
98 }
99 if (length(data) > 0)
100 pcap.write(data);
101 }
102
103 function open_ring(ring, poll) {
104 let ring_name =` ${ring.proc_name}:${ring.ring_name}`;
105 let ref = udebug.get_ring(ring);
106
107 if (!ref)
108 return null;
109 if (opts.duration)
110 ref.set_fetch_duration(opts.duration);
111 if (poll)
112 ref.set_poll_cb(() => { poll_data() });
113
114 let ring_id = ring.id + "";
115 ring = [ ring_name, ref ];
116 rings[ring_id] = ring;
117
118 return ring;
119 }
120
121 function open_output() {
122 if (!opts.output_file) {
123 _warn(`No output file\n`);
124 exit(1);
125 }
126 let out = opts.output_file;
127 if (out == "-")
128 out = null;
129
130 pcap = udebug.pcap_file(out);
131 if (!pcap) {
132 _warn(`Failed to open output\n`);
133 exit(1);
134 }
135 }
136
137 let cmds = {
138 list: function() {
139 for (let proc in procs) {
140 print(`Process ${proc}:\n`);
141 for (let ring in procs[proc])
142 print(` - ${ring.ring_name}\n`);
143 }
144 },
145 snapshot: function() {
146 open_output();
147
148 if (!length(selected)) {
149 _warn(`No available debug buffers\n`);
150 exit(1);
151 }
152
153 for (let ring in selected) {
154 if (!open_ring(ring)) {
155 _warn(`Failed to open ring ${ring.proc_name}:${ring.ring_name}\n`);
156 if (opts.force)
157 continue;
158
159 exit(1);
160 }
161 }
162
163 poll_data();
164 pcap.close();
165 },
166 set_flag: function() {
167 for (let ring in selected) {
168 if (!length(ring.flags))
169 continue;
170
171 let mask = 0, set = 0;
172 for (let flag in ring.flags) {
173 for (let change in ARGV) {
174 change = split(change, "=", 2);
175 let name = change[0];
176 let val = !!int(change[1]);
177 if (flag[0] == name)
178 if (val)
179 set |= flag[1];
180 else
181 mask |= flag[1];
182 }
183 }
184
185 if (!(mask | set))
186 continue;
187
188 let r = open_ring(ring);
189 if (!r)
190 continue;
191
192 r[1].change_flags(mask, set);
193 }
194 },
195 get_flags: function() {
196 for (let ring in selected) {
197 if (!length(ring.flags))
198 continue;
199
200 let r = open_ring(ring);
201 if (!r)
202 continue;
203
204 print(`${r[0]}\n`);
205 let flags = r[1].get_flags();
206 for (let flag in ring.flags)
207 print(`\t${flag[0]}=${((flags & flag[1]) == flag[1]) ? 1 : 0 }\n`);
208 }
209 },
210 stream: function() {
211 open_output();
212
213 subscriber = ubus.subscriber((req) => {
214 let type = req.type;
215 let ring = req.data;
216 let ring_id = ring.id + "";
217 if (type == "remove") {
218 ring = rings[ring_id];
219 if (!ring)
220 return;
221
222 ring[1].close();
223 delete rings[ring_id];
224 } else if (type == "add") {
225 open_ring(ring, true);
226 poll_data();
227 }
228 });
229 subscriber.subscribe("udebug");
230 for (let ring in selected) {
231 if (!open_ring(ring, true)) {
232 _warn(`Failed to open ring ${ring_name}\n`);
233 if (opts.force)
234 continue;
235
236 exit(1);
237 }
238 }
239
240 let done = () => { uloop.done(); };
241 signal('SIGINT', done);
242 signal('SIGTERM', done);
243
244 poll_data();
245 delete opts.duration;
246 uloop.run();
247 }
248 };
249
250 let cmd = shift(ARGV);
251 if (!cmds[cmd])
252 usage();
253
254 let ring_list = ubus.call("udebug", "list");
255 if (!ring_list || !ring_list.results) {
256 warn("Failed to get ring buffer list from udebugd\n");
257 exit(1);
258 }
259
260 ring_list = ring_list.results;
261 for (let ring in ring_list) {
262 if (!ring_selected(ring))
263 continue;
264
265 let proc = procs[ring.proc_name];
266 if (!proc) {
267 proc = [];
268 procs[ring.proc_name] = proc;
269 }
270 push(proc, ring);
271 push(selected, ring);
272 }
273
274 if (cmd != "list" && !udebug.init(opts.socket)) {
275 _warn(`Failed to connect to udebug socket\n`);
276 exit(1);
277 }
278
279 cmds[cmd]();