]>
Commit | Line | Data |
---|---|---|
f4b28491 | 1 | // systemtap translator/driver |
ce286ff0 | 2 | // Copyright (C) 2005-2011 Red Hat Inc. |
f12b2552 | 3 | // Copyright (C) 2005 IBM Corp. |
77a5c1f9 | 4 | // Copyright (C) 2006 Intel Corporation. |
69c68955 FCE |
5 | // |
6 | // This file is part of systemtap, and is free software. You can | |
7 | // redistribute it and/or modify it under the terms of the GNU General | |
8 | // Public License (GPL); either version 2, or (at your option) any | |
9 | // later version. | |
2b066ec1 FCE |
10 | |
11 | #include "config.h" | |
85007c04 DB |
12 | #include "staptree.h" |
13 | #include "parse.h" | |
14 | #include "elaborate.h" | |
15 | #include "translate.h" | |
16 | #include "buildrun.h" | |
dc38c0ae | 17 | #include "session.h" |
85007c04 DB |
18 | #include "hash.h" |
19 | #include "cache.h" | |
20 | #include "util.h" | |
21 | #include "coveragedb.h" | |
22 | #include "rpm_finder.h" | |
23 | #include "task_finder.h" | |
24 | #include "csclient.h" | |
daa75206 | 25 | #include "remote.h" |
85007c04 | 26 | |
0f5d597d | 27 | #include "stap-probe.h" |
aa4d21c0 | 28 | |
24cb178f | 29 | #include <cstdlib> |
73267b89 | 30 | |
85007c04 DB |
31 | extern "C" { |
32 | #include <glob.h> | |
33 | #include <unistd.h> | |
34 | #include <signal.h> | |
35 | #include <sys/utsname.h> | |
36 | #include <sys/times.h> | |
37 | #include <sys/time.h> | |
38 | #include <sys/stat.h> | |
39 | #include <time.h> | |
40 | #include <unistd.h> | |
41 | } | |
42 | ||
43 | using namespace std; | |
44 | ||
40a50254 JS |
45 | static void |
46 | uniq_list(list<string>& l) | |
e2012a7a | 47 | { |
40a50254 JS |
48 | set<string> s; |
49 | list<string>::iterator i = l.begin(); | |
50 | while (i != l.end()) | |
51 | if (s.insert(*i).second) | |
52 | ++i; | |
53 | else | |
54 | i = l.erase(i); | |
85007c04 | 55 | } |
2dce8c42 | 56 | |
85007c04 DB |
57 | static void |
58 | printscript(systemtap_session& s, ostream& o) | |
59 | { | |
60 | if (s.listing_mode) | |
61 | { | |
62 | // We go through some heroic measures to produce clean output. | |
63 | // Record the alias and probe pointer as <name, set<derived_probe *> > | |
64 | map<string,set<derived_probe *> > probe_list; | |
6e683641 | 65 | |
85007c04 DB |
66 | // Pre-process the probe alias |
67 | for (unsigned i=0; i<s.probes.size(); i++) | |
68 | { | |
69 | if (pending_interrupts) return; | |
70 | ||
71 | derived_probe* p = s.probes[i]; | |
72 | // NB: p->basest() is not so interesting; | |
73 | // p->almost_basest() doesn't quite work, so ... | |
74 | vector<probe*> chain; | |
75 | p->collect_derivation_chain (chain); | |
76 | probe* second = (chain.size()>1) ? chain[chain.size()-2] : chain[0]; | |
77 | ||
7977a734 | 78 | if (s.verbose > 5) { |
85007c04 DB |
79 | p->printsig(cerr); cerr << endl; |
80 | cerr << "chain[" << chain.size() << "]:" << endl; | |
81 | for (unsigned j=0; j<chain.size(); j++) | |
82 | { | |
83 | cerr << " [" << j << "]: " << endl; | |
84 | cerr << "\tlocations[" << chain[j]->locations.size() << "]:" << endl; | |
85 | for (unsigned k=0; k<chain[j]->locations.size(); k++) | |
86 | { | |
87 | cerr << "\t [" << k << "]: "; | |
88 | chain[j]->locations[k]->print(cerr); | |
89 | cerr << endl; | |
90 | } | |
91 | const probe_alias *a = chain[j]->get_alias(); | |
92 | if (a) | |
93 | { | |
94 | cerr << "\taliases[" << a->alias_names.size() << "]:" << endl; | |
95 | for (unsigned k=0; k<a->alias_names.size(); k++) | |
96 | { | |
97 | cerr << "\t [" << k << "]: "; | |
98 | a->alias_names[k]->print(cerr); | |
99 | cerr << endl; | |
100 | } | |
101 | } | |
102 | } | |
7977a734 | 103 | } |
85007c04 DB |
104 | |
105 | stringstream tmps; | |
106 | const probe_alias *a = second->get_alias(); | |
107 | if (a) | |
108 | { | |
109 | assert (a->alias_names.size() >= 1); | |
110 | a->alias_names[0]->print(tmps); // XXX: [0] is arbitrary; perhaps print all | |
111 | } | |
112 | else | |
113 | { | |
114 | assert (second->locations.size() >= 1); | |
115 | second->locations[0]->print(tmps); // XXX: [0] is less arbitrary here, but still ... | |
116 | } | |
117 | string pp = tmps.str(); | |
118 | ||
119 | // Now duplicate-eliminate. An alias may have expanded to | |
120 | // several actual derived probe points, but we only want to | |
121 | // print the alias head name once. | |
122 | probe_list[pp].insert(p); | |
123 | } | |
124 | ||
125 | // print probe name and variables if there | |
126 | for (map<string, set<derived_probe *> >::iterator it=probe_list.begin(); it!=probe_list.end(); ++it) | |
127 | { | |
128 | o << it->first; // probe name or alias | |
129 | ||
130 | // Print the locals and arguments for -L mode only | |
131 | if (s.listing_mode_vars) | |
132 | { | |
133 | map<string,unsigned> var_count; // format <"name:type",count> | |
134 | map<string,unsigned> arg_count; | |
135 | list<string> var_list; | |
136 | list<string> arg_list; | |
137 | // traverse set<derived_probe *> to collect all locals and arguments | |
138 | for (set<derived_probe *>::iterator ix=it->second.begin(); ix!=it->second.end(); ++ix) | |
139 | { | |
140 | derived_probe* p = *ix; | |
141 | // collect available locals of the probe | |
142 | for (unsigned j=0; j<p->locals.size(); j++) | |
143 | { | |
144 | stringstream tmps; | |
145 | vardecl* v = p->locals[j]; | |
146 | v->printsig (tmps); | |
147 | var_count[tmps.str()]++; | |
148 | var_list.push_back(tmps.str()); | |
149 | } | |
150 | // collect arguments of the probe if there | |
151 | list<string> arg_set; | |
152 | p->getargs(arg_set); | |
153 | for (list<string>::iterator ia=arg_set.begin(); ia!=arg_set.end(); ++ia) { | |
154 | arg_count[*ia]++; | |
155 | arg_list.push_back(*ia); | |
156 | } | |
157 | } | |
158 | ||
159 | uniq_list(arg_list); | |
160 | uniq_list(var_list); | |
161 | ||
162 | // print the set-intersection only | |
163 | for (list<string>::iterator ir=var_list.begin(); ir!=var_list.end(); ++ir) | |
164 | if (var_count.find(*ir)->second == it->second.size()) // print locals | |
165 | o << " " << *ir; | |
166 | for (list<string>::iterator ir=arg_list.begin(); ir!=arg_list.end(); ++ir) | |
167 | if (arg_count.find(*ir)->second == it->second.size()) // print arguments | |
168 | o << " " << *ir; | |
169 | } | |
170 | o << endl; | |
171 | } | |
172 | } | |
173 | else | |
2dce8c42 | 174 | { |
85007c04 DB |
175 | if (s.embeds.size() > 0) |
176 | o << "# global embedded code" << endl; | |
177 | for (unsigned i=0; i<s.embeds.size(); i++) | |
178 | { | |
179 | if (pending_interrupts) return; | |
180 | embeddedcode* ec = s.embeds[i]; | |
181 | ec->print (o); | |
182 | o << endl; | |
183 | } | |
2dce8c42 | 184 | |
85007c04 DB |
185 | if (s.globals.size() > 0) |
186 | o << "# globals" << endl; | |
187 | for (unsigned i=0; i<s.globals.size(); i++) | |
188 | { | |
189 | if (pending_interrupts) return; | |
190 | vardecl* v = s.globals[i]; | |
191 | v->printsig (o); | |
192 | if (s.verbose && v->init) | |
193 | { | |
194 | o << " = "; | |
195 | v->init->print(o); | |
196 | } | |
197 | o << endl; | |
198 | } | |
199 | ||
200 | if (s.functions.size() > 0) | |
201 | o << "# functions" << endl; | |
202 | for (map<string,functiondecl*>::iterator it = s.functions.begin(); it != s.functions.end(); it++) | |
203 | { | |
204 | if (pending_interrupts) return; | |
205 | functiondecl* f = it->second; | |
206 | f->printsig (o); | |
207 | o << endl; | |
208 | if (f->locals.size() > 0) | |
209 | o << " # locals" << endl; | |
210 | for (unsigned j=0; j<f->locals.size(); j++) | |
211 | { | |
212 | vardecl* v = f->locals[j]; | |
213 | o << " "; | |
214 | v->printsig (o); | |
215 | o << endl; | |
216 | } | |
217 | if (s.verbose) | |
218 | { | |
219 | f->body->print (o); | |
220 | o << endl; | |
221 | } | |
222 | } | |
223 | ||
224 | if (s.probes.size() > 0) | |
225 | o << "# probes" << endl; | |
226 | for (unsigned i=0; i<s.probes.size(); i++) | |
227 | { | |
228 | if (pending_interrupts) return; | |
229 | derived_probe* p = s.probes[i]; | |
230 | p->printsig (o); | |
231 | o << endl; | |
232 | if (p->locals.size() > 0) | |
233 | o << " # locals" << endl; | |
234 | for (unsigned j=0; j<p->locals.size(); j++) | |
235 | { | |
236 | vardecl* v = p->locals[j]; | |
237 | o << " "; | |
238 | v->printsig (o); | |
239 | o << endl; | |
240 | } | |
241 | if (s.verbose) | |
242 | { | |
243 | p->body->print (o); | |
244 | o << endl; | |
245 | } | |
246 | } | |
2dce8c42 | 247 | } |
85007c04 | 248 | } |
e2012a7a | 249 | |
e2012a7a | 250 | |
85007c04 DB |
251 | int pending_interrupts; |
252 | ||
253 | extern "C" | |
254 | void handle_interrupt (int sig) | |
255 | { | |
256 | kill_stap_spawn(sig); | |
257 | pending_interrupts ++; | |
258 | if (pending_interrupts > 1) // XXX: should be configurable? time-based? | |
259 | { | |
260 | char msg[] = "Too many interrupts received, exiting.\n"; | |
261 | int rc = write (2, msg, sizeof(msg)-1); | |
262 | if (rc) {/* Do nothing; we don't care if our last gasp went out. */ ;} | |
263 | _exit (1); | |
264 | } | |
265 | } | |
266 | ||
267 | ||
268 | void | |
269 | setup_signals (sighandler_t handler) | |
270 | { | |
271 | struct sigaction sa; | |
272 | ||
273 | sa.sa_handler = handler; | |
274 | sigemptyset (&sa.sa_mask); | |
275 | if (handler != SIG_IGN) | |
276 | { | |
277 | sigaddset (&sa.sa_mask, SIGHUP); | |
278 | sigaddset (&sa.sa_mask, SIGPIPE); | |
279 | sigaddset (&sa.sa_mask, SIGINT); | |
280 | sigaddset (&sa.sa_mask, SIGTERM); | |
281 | } | |
282 | sa.sa_flags = SA_RESTART; | |
283 | ||
284 | sigaction (SIGHUP, &sa, NULL); | |
285 | sigaction (SIGPIPE, &sa, NULL); | |
286 | sigaction (SIGINT, &sa, NULL); | |
287 | sigaction (SIGTERM, &sa, NULL); | |
e2012a7a | 288 | } |
6e683641 | 289 | |
85007c04 DB |
290 | int parse_kernel_config (systemtap_session &s) |
291 | { | |
292 | // PR10702: pull config options | |
293 | string kernel_config_file = s.kernel_build_tree + "/.config"; | |
294 | struct stat st; | |
295 | int rc = stat(kernel_config_file.c_str(), &st); | |
296 | if (rc != 0) | |
76c87907 LB |
297 | { |
298 | clog << "Checking \"" << kernel_config_file << "\" failed: " << strerror(errno) << endl; | |
299 | find_devel_rpms(s, s.kernel_build_tree.c_str()); | |
300 | missing_rpm_list_print(s,"-devel"); | |
85007c04 DB |
301 | return rc; |
302 | } | |
6861e056 | 303 | |
85007c04 DB |
304 | ifstream kcf (kernel_config_file.c_str()); |
305 | string line; | |
306 | while (getline (kcf, line)) | |
307 | { | |
308 | if (!startswith(line, "CONFIG_")) continue; | |
309 | size_t off = line.find('='); | |
310 | if (off == string::npos) continue; | |
311 | string key = line.substr(0, off); | |
312 | string value = line.substr(off+1, string::npos); | |
313 | s.kernel_config[key] = value; | |
314 | } | |
315 | if (s.verbose > 2) | |
316 | clog << "Parsed kernel \"" << kernel_config_file << "\", number of tuples: " << s.kernel_config.size() << endl; | |
317 | ||
318 | kcf.close(); | |
319 | return 0; | |
320 | } | |
321 | ||
322 | ||
323 | int parse_kernel_exports (systemtap_session &s) | |
324 | { | |
325 | string kernel_exports_file = s.kernel_build_tree + "/Module.symvers"; | |
326 | struct stat st; | |
327 | int rc = stat(kernel_exports_file.c_str(), &st); | |
328 | if (rc != 0) | |
329 | { | |
330 | clog << "Checking \"" << kernel_exports_file << "\" failed: " << strerror(errno) << endl | |
331 | << "Ensure kernel development headers & makefiles are installed." << endl; | |
332 | return rc; | |
333 | } | |
334 | ||
335 | ifstream kef (kernel_exports_file.c_str()); | |
336 | string line; | |
337 | while (getline (kef, line)) | |
338 | { | |
339 | vector<string> tokens; | |
340 | tokenize (line, tokens, "\t"); | |
341 | if (tokens.size() == 4 && | |
342 | tokens[2] == "vmlinux" && | |
343 | tokens[3].substr(0,13) == string("EXPORT_SYMBOL")) | |
344 | s.kernel_exports.insert (tokens[1]); | |
345 | } | |
346 | if (s.verbose > 2) | |
347 | clog << "Parsed kernel \"" << kernel_exports_file << "\", number of vmlinux exports: " << s.kernel_exports.size() << endl; | |
348 | ||
349 | kef.close(); | |
350 | return 0; | |
351 | } | |
352 | ||
353 | ||
354 | static void | |
355 | create_temp_dir (systemtap_session &s) | |
356 | { | |
357 | // Create a temporary directory to build within. | |
358 | // Be careful with this, as "tmpdir" is "rm -rf"'d at the end. | |
359 | const char* tmpdir_env = getenv("TMPDIR"); | |
360 | if (! tmpdir_env) | |
361 | tmpdir_env = "/tmp"; | |
362 | ||
363 | string stapdir = "/stapXXXXXX"; | |
364 | string tmpdirt = tmpdir_env + stapdir; | |
365 | mode_t mask = umask(0); | |
366 | const char *tmpdir_name = mkdtemp((char *)tmpdirt.c_str()); | |
367 | umask(mask); | |
368 | if (! tmpdir_name) | |
369 | { | |
370 | const char* e = strerror (errno); | |
371 | cerr << "ERROR: cannot create temporary directory (\"" << tmpdirt << "\"): " << e << endl; | |
372 | exit (1); // die | |
373 | } | |
374 | else | |
375 | s.tmpdir = tmpdir_name; | |
376 | ||
377 | if (s.verbose>1) | |
378 | clog << "Created temporary directory \"" << s.tmpdir << "\"" << endl; | |
379 | } | |
380 | ||
381 | static void | |
382 | remove_temp_dir (systemtap_session &s) | |
383 | { | |
384 | if (s.tmpdir != "") | |
385 | { | |
386 | if (s.keep_tmpdir) | |
387 | // NB: the format of this message needs to match the expectations | |
388 | // of stap-server-connect.c. | |
389 | clog << "Keeping temporary directory \"" << s.tmpdir << "\"" << endl; | |
390 | else | |
391 | { | |
392 | // Ignore signals while we're deleting the temporary directory. | |
393 | setup_signals (SIG_IGN); | |
394 | ||
395 | // Remove the temporary directory. | |
396 | string cleanupcmd = "rm -rf "; | |
397 | cleanupcmd += s.tmpdir; | |
398 | ||
399 | (void) stap_system (s.verbose, cleanupcmd); | |
400 | } | |
401 | } | |
402 | } | |
403 | ||
ce286ff0 | 404 | // Compilation passes 0 through 4 |
85007c04 DB |
405 | static int |
406 | passes_0_4 (systemtap_session &s) | |
407 | { | |
408 | int rc = 0; | |
daa75206 | 409 | |
85007c04 DB |
410 | // Create a temporary directory to build within. |
411 | // Be careful with this, as "s.tmpdir" is "rm -rf"'d at the end. | |
412 | create_temp_dir (s); | |
413 | ||
414 | // Perform passes 0 through 4 using a compile server? | |
415 | if (! s.specified_servers.empty ()) | |
416 | { | |
0d1534e8 | 417 | #if HAVE_NSS |
85007c04 DB |
418 | compile_server_client client (s); |
419 | return client.passes_0_4 (); | |
0d1534e8 | 420 | #else |
4c010175 | 421 | cerr << "WARNING: Without NSS, using a compile-server is not supported by this version of systemtap" << endl; |
ce286ff0 DB |
422 | // If this was an attempt to use a server after a local compile failed |
423 | // then a local compile will fail again. | |
424 | if (s.try_server ()) | |
425 | return 1; | |
0d1534e8 | 426 | #endif |
85007c04 DB |
427 | } |
428 | ||
429 | // PASS 0: setting up | |
430 | s.verbose = s.perpass_verbose[0]; | |
0f5d597d | 431 | PROBE1(stap, pass0__start, &s); |
85007c04 | 432 | |
85007c04 DB |
433 | // For PR1477, we used to override $PATH and $LC_ALL and other stuff |
434 | // here. We seem to use complete pathnames in | |
435 | // buildrun.cxx/tapsets.cxx now, so this is not necessary. Further, | |
436 | // it interferes with util.cxx:find_executable(), used for $PATH | |
437 | // resolution. | |
438 | ||
439 | s.kernel_base_release.assign(s.kernel_release, 0, s.kernel_release.find('-')); | |
440 | ||
441 | // arguments parsed; get down to business | |
442 | if (s.verbose > 1) | |
443 | { | |
444 | s.version (); | |
445 | clog << "Session arch: " << s.architecture | |
446 | << " release: " << s.kernel_release | |
447 | << endl; | |
448 | } | |
449 | ||
450 | // Now that no further changes to s.kernel_build_tree can occur, let's use it. | |
ce286ff0 DB |
451 | if ((rc = parse_kernel_config (s)) != 0) |
452 | { | |
453 | // Try again with a server | |
454 | s.set_try_server (); | |
455 | return rc; | |
456 | } | |
85007c04 | 457 | |
ce286ff0 DB |
458 | if ((rc = parse_kernel_exports (s)) != 0) |
459 | { | |
460 | // Try again with a server | |
461 | s.set_try_server (); | |
462 | return rc; | |
463 | } | |
85007c04 DB |
464 | |
465 | // Create the name of the C source file within the temporary | |
466 | // directory. | |
467 | s.translated_source = string(s.tmpdir) + "/" + s.module_name + ".c"; | |
468 | ||
469 | // Set up our handler to catch routine signals, to allow clean | |
470 | // and reasonably timely exit. | |
471 | setup_signals(&handle_interrupt); | |
472 | ||
0f5d597d | 473 | PROBE1(stap, pass0__end, &s); |
85007c04 DB |
474 | |
475 | struct tms tms_before; | |
476 | times (& tms_before); | |
477 | struct timeval tv_before; | |
478 | gettimeofday (&tv_before, NULL); | |
479 | ||
480 | // PASS 1a: PARSING USER SCRIPT | |
0f5d597d | 481 | PROBE1(stap, pass1a__start, &s); |
85007c04 DB |
482 | |
483 | struct stat user_file_stat; | |
484 | int user_file_stat_rc = -1; | |
485 | ||
486 | if (s.script_file == "-") | |
487 | { | |
488 | s.user_file = parse (s, cin, s.guru_mode); | |
489 | user_file_stat_rc = fstat (STDIN_FILENO, & user_file_stat); | |
490 | } | |
491 | else if (s.script_file != "") | |
492 | { | |
493 | s.user_file = parse (s, s.script_file, s.guru_mode); | |
494 | user_file_stat_rc = stat (s.script_file.c_str(), & user_file_stat); | |
495 | } | |
496 | else | |
497 | { | |
498 | istringstream ii (s.cmdline_script); | |
499 | s.user_file = parse (s, ii, s.guru_mode); | |
500 | } | |
501 | if (s.user_file == 0) | |
ce286ff0 DB |
502 | { |
503 | // Syntax errors already printed. | |
504 | rc ++; | |
505 | // Don't bother trying to compile with a server. | |
506 | s.set_try_server (systemtap_session::dont_try_server); | |
507 | } | |
85007c04 DB |
508 | |
509 | // Construct arch / kernel-versioning search path | |
510 | vector<string> version_suffixes; | |
511 | string kvr = s.kernel_release; | |
512 | const string& arch = s.architecture; | |
513 | // add full kernel-version-release (2.6.NN-FOOBAR) + arch | |
514 | version_suffixes.push_back ("/" + kvr + "/" + arch); | |
515 | version_suffixes.push_back ("/" + kvr); | |
516 | // add kernel version (2.6.NN) + arch | |
517 | if (kvr != s.kernel_base_release) { | |
518 | kvr = s.kernel_base_release; | |
519 | version_suffixes.push_back ("/" + kvr + "/" + arch); | |
520 | version_suffixes.push_back ("/" + kvr); | |
521 | } | |
522 | // add kernel family (2.6) + arch | |
523 | string::size_type dot1_index = kvr.find ('.'); | |
524 | string::size_type dot2_index = kvr.rfind ('.'); | |
525 | while (dot2_index > dot1_index && dot2_index != string::npos) { | |
526 | kvr.erase(dot2_index); | |
527 | version_suffixes.push_back ("/" + kvr + "/" + arch); | |
528 | version_suffixes.push_back ("/" + kvr); | |
529 | dot2_index = kvr.rfind ('.'); | |
530 | } | |
531 | // add architecture search path | |
532 | version_suffixes.push_back("/" + arch); | |
533 | // add empty string as last element | |
534 | version_suffixes.push_back (""); | |
535 | ||
536 | // PASS 1b: PARSING LIBRARY SCRIPTS | |
0f5d597d | 537 | PROBE1(stap, pass1b__start, &s); |
85007c04 | 538 | |
ad7c8e43 FCE |
539 | set<pair<dev_t, ino_t> > seen_library_files; |
540 | ||
85007c04 DB |
541 | for (unsigned i=0; i<s.include_path.size(); i++) |
542 | { | |
543 | // now iterate upon it | |
544 | for (unsigned k=0; k<version_suffixes.size(); k++) | |
545 | { | |
546 | glob_t globbuf; | |
547 | string dir = s.include_path[i] + version_suffixes[k] + "/*.stp"; | |
548 | int r = glob(dir.c_str (), 0, NULL, & globbuf); | |
549 | if (r == GLOB_NOSPACE || r == GLOB_ABORTED) | |
ce286ff0 DB |
550 | { |
551 | rc ++; | |
552 | // Try again with a server. | |
553 | s.set_try_server (); | |
554 | } | |
555 | // GLOB_NOMATCH is acceptable | |
85007c04 | 556 | |
ad7c8e43 | 557 | unsigned prev_s_library_files = s.library_files.size(); |
85007c04 DB |
558 | |
559 | for (unsigned j=0; j<globbuf.gl_pathc; j++) | |
560 | { | |
561 | if (pending_interrupts) | |
562 | break; | |
563 | ||
85007c04 DB |
564 | struct stat tapset_file_stat; |
565 | int stat_rc = stat (globbuf.gl_pathv[j], & tapset_file_stat); | |
566 | if (stat_rc == 0 && user_file_stat_rc == 0 && | |
567 | user_file_stat.st_dev == tapset_file_stat.st_dev && | |
568 | user_file_stat.st_ino == tapset_file_stat.st_ino) | |
569 | { | |
ad7c8e43 | 570 | cerr << "usage error: tapset file '" << globbuf.gl_pathv[j] |
85007c04 DB |
571 | << "' cannot be run directly as a session script." << endl; |
572 | rc ++; | |
ce286ff0 DB |
573 | // Don't bother trying to compile with a server. |
574 | s.set_try_server (systemtap_session::dont_try_server); | |
85007c04 DB |
575 | } |
576 | ||
ad7c8e43 FCE |
577 | // PR11949: duplicate-eliminate tapset files |
578 | if (stat_rc == 0) | |
579 | { | |
580 | pair<dev_t,ino_t> here = make_pair(tapset_file_stat.st_dev, | |
581 | tapset_file_stat.st_ino); | |
582 | if (seen_library_files.find(here) != seen_library_files.end()) | |
583 | continue; | |
584 | seen_library_files.insert (here); | |
585 | } | |
586 | ||
587 | // XXX: privilege only for /usr/share/systemtap? | |
588 | stapfile* f = parse (s, globbuf.gl_pathv[j], true); | |
589 | if (f == 0 && !s.suppress_warnings) | |
590 | s.print_warning("tapset '" + string(globbuf.gl_pathv[j]) | |
591 | + "' has errors, and will be skipped."); | |
592 | else | |
593 | s.library_files.push_back (f); | |
85007c04 DB |
594 | } |
595 | ||
ad7c8e43 FCE |
596 | unsigned next_s_library_files = s.library_files.size(); |
597 | if (s.verbose>1 && globbuf.gl_pathc > 0) | |
598 | clog << "Searched \"" << dir << "\"," | |
599 | << " found " << globbuf.gl_pathc | |
600 | << " processed " << (next_s_library_files-prev_s_library_files) << endl; | |
601 | ||
85007c04 DB |
602 | globfree (& globbuf); |
603 | } | |
604 | } | |
605 | if (s.num_errors()) | |
ce286ff0 DB |
606 | { |
607 | rc ++; | |
608 | // Try again with a server. | |
609 | s.set_try_server (); | |
610 | } | |
85007c04 DB |
611 | |
612 | if (rc == 0 && s.last_pass == 1) | |
613 | { | |
614 | cout << "# parse tree dump" << endl; | |
615 | s.user_file->print (cout); | |
616 | cout << endl; | |
617 | if (s.verbose) | |
618 | for (unsigned i=0; i<s.library_files.size(); i++) | |
619 | { | |
620 | s.library_files[i]->print (cout); | |
621 | cout << endl; | |
622 | } | |
623 | } | |
624 | ||
625 | struct tms tms_after; | |
626 | times (& tms_after); | |
627 | unsigned _sc_clk_tck = sysconf (_SC_CLK_TCK); | |
628 | struct timeval tv_after; | |
629 | gettimeofday (&tv_after, NULL); | |
630 | ||
631 | #define TIMESPRINT "in " << \ | |
632 | (tms_after.tms_cutime + tms_after.tms_utime \ | |
633 | - tms_before.tms_cutime - tms_before.tms_utime) * 1000 / (_sc_clk_tck) << "usr/" \ | |
634 | << (tms_after.tms_cstime + tms_after.tms_stime \ | |
635 | - tms_before.tms_cstime - tms_before.tms_stime) * 1000 / (_sc_clk_tck) << "sys/" \ | |
636 | << ((tv_after.tv_sec - tv_before.tv_sec) * 1000 + \ | |
637 | ((long)tv_after.tv_usec - (long)tv_before.tv_usec) / 1000) << "real ms." | |
638 | ||
639 | // syntax errors, if any, are already printed | |
640 | if (s.verbose) | |
641 | { | |
642 | clog << "Pass 1: parsed user script and " | |
643 | << s.library_files.size() | |
644 | << " library script(s) " | |
645 | << getmemusage() | |
646 | << TIMESPRINT | |
647 | << endl; | |
648 | } | |
649 | ||
650 | if (rc && !s.listing_mode) | |
651 | cerr << "Pass 1: parse failed. " | |
652 | << "Try again with another '--vp 1' option." | |
653 | << endl; | |
654 | ||
0f5d597d | 655 | PROBE1(stap, pass1__end, &s); |
85007c04 DB |
656 | |
657 | if (rc || s.last_pass == 1 || pending_interrupts) return rc; | |
658 | ||
659 | times (& tms_before); | |
660 | gettimeofday (&tv_before, NULL); | |
661 | ||
662 | // PASS 2: ELABORATION | |
663 | s.verbose = s.perpass_verbose[1]; | |
0f5d597d | 664 | PROBE1(stap, pass2__start, &s); |
85007c04 | 665 | rc = semantic_pass (s); |
ce286ff0 DB |
666 | if (rc) |
667 | { | |
668 | // Try again with a server. | |
669 | s.set_try_server (); | |
670 | } | |
85007c04 DB |
671 | |
672 | if (s.listing_mode || (rc == 0 && s.last_pass == 2)) | |
673 | printscript(s, cout); | |
674 | ||
675 | times (& tms_after); | |
676 | gettimeofday (&tv_after, NULL); | |
677 | ||
678 | if (s.verbose) clog << "Pass 2: analyzed script: " | |
679 | << s.probes.size() << " probe(s), " | |
680 | << s.functions.size() << " function(s), " | |
681 | << s.embeds.size() << " embed(s), " | |
682 | << s.globals.size() << " global(s) " | |
683 | << getmemusage() | |
684 | << TIMESPRINT | |
685 | << endl; | |
686 | ||
ce286ff0 | 687 | if (rc && !s.listing_mode && !s.try_server ()) |
85007c04 DB |
688 | cerr << "Pass 2: analysis failed. " |
689 | << "Try again with another '--vp 01' option." | |
690 | << endl; | |
691 | ||
692 | /* Print out list of missing files. XXX should be "if (rc)" ? */ | |
76c87907 | 693 | missing_rpm_list_print(s,"-debuginfo"); |
85007c04 | 694 | |
0f5d597d | 695 | PROBE1(stap, pass2__end, &s); |
85007c04 DB |
696 | |
697 | if (rc || s.listing_mode || s.last_pass == 2 || pending_interrupts) return rc; | |
698 | ||
d13bcfd8 | 699 | rc = prepare_translate_pass (s); |
ce286ff0 DB |
700 | if (rc) |
701 | { | |
702 | // Try again with a server. | |
703 | s.set_try_server (); | |
704 | } | |
d13bcfd8 JS |
705 | if (rc || pending_interrupts) return rc; |
706 | ||
85007c04 DB |
707 | // Generate hash. There isn't any point in generating the hash |
708 | // if last_pass is 2, since we'll quit before using it. | |
709 | if (s.use_script_cache) | |
710 | { | |
711 | ostringstream o; | |
712 | unsigned saved_verbose; | |
713 | ||
714 | { | |
715 | // Make sure we're in verbose mode, so that printscript() | |
716 | // will output function/probe bodies. | |
717 | saved_verbose = s.verbose; | |
718 | s.verbose = 3; | |
719 | printscript(s, o); // Print script to 'o' | |
720 | s.verbose = saved_verbose; | |
721 | } | |
722 | ||
723 | // Generate hash | |
724 | find_script_hash (s, o.str()); | |
725 | ||
726 | // See if we can use cached source/module. | |
727 | if (get_script_from_cache(s)) | |
728 | { | |
729 | // If our last pass isn't 5, we're done (since passes 3 and | |
730 | // 4 just generate what we just pulled out of the cache). | |
731 | if (s.last_pass < 5 || pending_interrupts) return rc; | |
732 | ||
733 | // Short-circuit to pass 5. | |
734 | return 0; | |
735 | } | |
736 | } | |
737 | ||
738 | // PASS 3: TRANSLATION | |
739 | s.verbose = s.perpass_verbose[2]; | |
740 | times (& tms_before); | |
741 | gettimeofday (&tv_before, NULL); | |
0f5d597d | 742 | PROBE1(stap, pass3__start, &s); |
85007c04 DB |
743 | |
744 | rc = translate_pass (s); | |
ce286ff0 DB |
745 | if (rc) |
746 | { | |
747 | // Try again with a server. | |
748 | s.set_try_server (); | |
749 | } | |
750 | else if (s.last_pass == 3) | |
85007c04 DB |
751 | { |
752 | ifstream i (s.translated_source.c_str()); | |
753 | cout << i.rdbuf(); | |
754 | } | |
755 | ||
756 | times (& tms_after); | |
757 | gettimeofday (&tv_after, NULL); | |
758 | ||
759 | if (s.verbose) clog << "Pass 3: translated to C into \"" | |
760 | << s.translated_source | |
761 | << "\" " | |
762 | << getmemusage() | |
763 | << TIMESPRINT | |
764 | << endl; | |
765 | ||
ce286ff0 | 766 | if (rc && ! s.try_server ()) |
85007c04 DB |
767 | cerr << "Pass 3: translation failed. " |
768 | << "Try again with another '--vp 001' option." | |
769 | << endl; | |
770 | ||
0f5d597d | 771 | PROBE1(stap, pass3__end, &s); |
85007c04 DB |
772 | |
773 | if (rc || s.last_pass == 3 || pending_interrupts) return rc; | |
774 | ||
775 | // PASS 4: COMPILATION | |
776 | s.verbose = s.perpass_verbose[3]; | |
777 | times (& tms_before); | |
778 | gettimeofday (&tv_before, NULL); | |
0f5d597d | 779 | PROBE1(stap, pass4__start, &s); |
85007c04 DB |
780 | |
781 | if (s.use_cache) | |
782 | { | |
783 | find_stapconf_hash(s); | |
784 | get_stapconf_from_cache(s); | |
785 | } | |
786 | rc = compile_pass (s); | |
ce286ff0 DB |
787 | if (rc) |
788 | { | |
789 | // Try again with a server. | |
790 | s.set_try_server (); | |
791 | } | |
792 | else if (s.last_pass == 4) | |
85007c04 DB |
793 | { |
794 | cout << ((s.hash_path == "") ? (s.module_name + string(".ko")) : s.hash_path); | |
795 | cout << endl; | |
796 | } | |
797 | ||
798 | times (& tms_after); | |
799 | gettimeofday (&tv_after, NULL); | |
800 | ||
801 | if (s.verbose) clog << "Pass 4: compiled C into \"" | |
802 | << s.module_name << ".ko" | |
803 | << "\" " | |
804 | << TIMESPRINT | |
805 | << endl; | |
806 | ||
ce286ff0 | 807 | if (rc && ! s.try_server ()) |
85007c04 DB |
808 | cerr << "Pass 4: compilation failed. " |
809 | << "Try again with another '--vp 0001' option." | |
810 | << endl; | |
811 | else | |
812 | { | |
813 | // Update cache. Cache cleaning is kicked off at the beginning of this function. | |
814 | if (s.use_script_cache) | |
815 | add_script_to_cache(s); | |
816 | if (s.use_cache) | |
817 | add_stapconf_to_cache(s); | |
818 | ||
819 | // We may need to save the module in $CWD if the cache was | |
820 | // inaccessible for some reason. | |
821 | if (! s.use_script_cache && s.last_pass == 4) | |
822 | s.save_module = true; | |
823 | ||
824 | // Copy module to the current directory. | |
825 | if (s.save_module && !pending_interrupts) | |
826 | { | |
827 | string module_src_path = s.tmpdir + "/" + s.module_name + ".ko"; | |
828 | string module_dest_path = s.module_name + ".ko"; | |
829 | copy_file(module_src_path, module_dest_path, s.verbose > 1); | |
830 | } | |
831 | } | |
832 | ||
0f5d597d | 833 | PROBE1(stap, pass4__end, &s); |
85007c04 DB |
834 | |
835 | return rc; | |
836 | } | |
837 | ||
838 | static int | |
ebff2ed0 | 839 | pass_5 (systemtap_session &s, vector<remote*> targets) |
85007c04 DB |
840 | { |
841 | // PASS 5: RUN | |
842 | s.verbose = s.perpass_verbose[4]; | |
843 | struct tms tms_before; | |
844 | times (& tms_before); | |
845 | struct timeval tv_before; | |
846 | gettimeofday (&tv_before, NULL); | |
847 | // NB: this message is a judgement call. The other passes don't emit | |
848 | // a "hello, I'm starting" message, but then the others aren't interactive | |
849 | // and don't take an indefinite amount of time. | |
0f5d597d | 850 | PROBE1(stap, pass5__start, &s); |
85007c04 | 851 | if (s.verbose) clog << "Pass 5: starting run." << endl; |
94a8b578 | 852 | int rc = 0; // XXX with multiple targets, need to deal with partial failure |
ebff2ed0 JS |
853 | for (unsigned i = 0; i < targets.size() && !pending_interrupts; ++i) |
854 | rc |= targets[i]->start(); | |
855 | for (unsigned i = 0; i < targets.size(); ++i) | |
856 | rc |= targets[i]->finish(); | |
85007c04 DB |
857 | struct tms tms_after; |
858 | times (& tms_after); | |
859 | unsigned _sc_clk_tck = sysconf (_SC_CLK_TCK); | |
860 | struct timeval tv_after; | |
861 | gettimeofday (&tv_after, NULL); | |
862 | if (s.verbose) clog << "Pass 5: run completed " | |
863 | << TIMESPRINT | |
864 | << endl; | |
865 | ||
866 | if (rc) | |
867 | cerr << "Pass 5: run failed. " | |
868 | << "Try again with another '--vp 00001' option." | |
869 | << endl; | |
870 | else | |
871 | // Interrupting pass-5 to quit is normal, so we want an EXIT_SUCCESS below. | |
872 | pending_interrupts = 0; | |
873 | ||
0f5d597d | 874 | PROBE1(stap, pass5__end, &s); |
85007c04 DB |
875 | |
876 | return rc; | |
877 | } | |
878 | ||
879 | static void | |
880 | cleanup (systemtap_session &s, int rc) | |
881 | { | |
882 | // PASS 6: cleaning up | |
0f5d597d | 883 | PROBE1(stap, pass6__start, &s); |
85007c04 | 884 | |
ebff2ed0 JS |
885 | for (systemtap_session::session_map_t::iterator it = s.subsessions.begin(); |
886 | it != s.subsessions.end(); ++it) | |
887 | cleanup (*it->second, rc); | |
888 | ||
85007c04 DB |
889 | // update the database information |
890 | if (!rc && s.tapset_compile_coverage && !pending_interrupts) { | |
891 | #ifdef HAVE_LIBSQLITE3 | |
892 | update_coverage_db(s); | |
893 | #else | |
894 | cerr << "Coverage database not available without libsqlite3" << endl; | |
895 | #endif | |
896 | } | |
897 | ||
898 | // Clean up temporary directory. Obviously, be careful with this. | |
899 | remove_temp_dir (s); | |
900 | ||
0f5d597d | 901 | PROBE1(stap, pass6__end, &s); |
85007c04 DB |
902 | } |
903 | ||
ce286ff0 DB |
904 | static int |
905 | passes_0_4_again_with_server (systemtap_session &s) | |
906 | { | |
907 | // Not a server and not already using a server. | |
908 | assert (! s.client_options); | |
909 | assert (s.specified_servers.empty ()); | |
910 | ||
911 | // Specify default server(s). | |
912 | s.specified_servers.push_back (""); | |
913 | ||
914 | // Remove the previous temporary directory and start fresh. | |
915 | remove_temp_dir (s); | |
916 | ||
917 | // Try to compile again, using the server | |
918 | clog << "Attempting compilation using a compile server" | |
919 | << endl; | |
920 | int rc = passes_0_4 (s); | |
921 | return rc; | |
922 | } | |
923 | ||
85007c04 DB |
924 | int |
925 | main (int argc, char * const argv []) | |
926 | { | |
927 | // Initialize defaults. | |
928 | systemtap_session s; | |
929 | ||
930 | // Process the command line. | |
931 | int rc = s.parse_cmdline (argc, argv); | |
932 | if (rc != 0) | |
933 | exit (rc); | |
934 | ||
935 | // Check for options conflicts. Exits if errors are detected. | |
936 | s.check_options (argc, argv); | |
937 | ||
938 | // If requested, query server status. This is independent of other tasks. | |
939 | query_server_status (s); | |
940 | ||
c77af0d0 DB |
941 | // If requested, manage trust of servers. This is independent of other tasks. |
942 | manage_server_trust (s); | |
943 | ||
85007c04 DB |
944 | // Run the passes only if a script has been specified. The requirement for |
945 | // a script has already been checked in systemtap_session::check_options. | |
946 | if (s.have_script) | |
947 | { | |
ebff2ed0 JS |
948 | vector<remote*> targets; |
949 | if (s.remote_uris.empty()) | |
950 | { | |
951 | remote* target = remote::create(s, "direct"); | |
952 | if (target) | |
953 | targets.push_back(target); | |
954 | else | |
955 | rc = 1; | |
956 | } | |
daa75206 | 957 | else |
ebff2ed0 JS |
958 | for (unsigned i = 0; i < s.remote_uris.size(); ++i) |
959 | { | |
960 | remote *target = remote::create(s, s.remote_uris[i]); | |
961 | if (target) | |
962 | targets.push_back(target); | |
963 | else | |
964 | { | |
965 | rc = 1; | |
966 | break; | |
967 | } | |
968 | } | |
969 | ||
970 | // Run passes 0-4 for each unique session, | |
971 | // either locally or using a compile-server. | |
daa75206 | 972 | if (rc == 0) |
ebff2ed0 JS |
973 | { |
974 | set<systemtap_session*> sessions; | |
975 | for (unsigned i = 0; i < targets.size(); ++i) | |
976 | sessions.insert(targets[i]->get_session()); | |
977 | for (set<systemtap_session*>::iterator it = sessions.begin(); | |
978 | it != sessions.end(); ++it) | |
ce286ff0 DB |
979 | { |
980 | (*it)->init_try_server (); | |
981 | if ((rc = passes_0_4 (**it))) | |
982 | { | |
983 | // Compilation failed. | |
984 | // Try again using a server if appropriate. | |
985 | if ((*it)->try_server ()) | |
986 | rc = passes_0_4_again_with_server (**it); | |
987 | if (rc) | |
988 | break; | |
989 | } | |
990 | } | |
ebff2ed0 | 991 | } |
85007c04 DB |
992 | |
993 | // Run pass 5, if requested | |
994 | if (rc == 0 && s.last_pass >= 5 && ! pending_interrupts) | |
ebff2ed0 JS |
995 | rc = pass_5 (s, targets); |
996 | ||
997 | for (unsigned i = 0; i < targets.size(); ++i) | |
998 | delete targets[i]; | |
85007c04 DB |
999 | } |
1000 | ||
1001 | // Pass 6. Cleanup | |
1002 | cleanup (s, rc); | |
1003 | ||
1004 | return (rc||pending_interrupts) ? EXIT_FAILURE : EXIT_SUCCESS; | |
1005 | } | |
1006 | ||
1007 | /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */ |