1 : // systemtap translator/driver
2 : // Copyright (C) 2005-2008 Red Hat Inc.
3 : // Copyright (C) 2005 IBM Corp.
4 : // Copyright (C) 2006 Intel Corporation.
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.
10 :
11 : #include "config.h"
12 : #include "staptree.h"
13 : #include "parse.h"
14 : #include "elaborate.h"
15 : #include "translate.h"
16 : #include "buildrun.h"
17 : #include "session.h"
18 : #include "hash.h"
19 : #include "cache.h"
20 : #include "util.h"
21 : #include "coveragedb.h"
22 :
23 : #include <iostream>
24 : #include <fstream>
25 : #include <sstream>
26 : #include <cerrno>
27 : #include <cstdlib>
28 :
29 : extern "C" {
30 : #include <glob.h>
31 : #include <unistd.h>
32 : #include <signal.h>
33 : #include <sys/utsname.h>
34 : #include <sys/times.h>
35 : #include <sys/time.h>
36 : #include <sys/stat.h>
37 : #include <time.h>
38 : #include <elfutils/libdwfl.h>
39 : }
40 :
41 : using namespace std;
42 :
43 :
44 : void
45 13 : version ()
46 : {
47 : clog
48 : << "SystemTap translator/driver "
49 : << "(version " << VERSION << "/" << dwfl_version (NULL)
50 : << " built " << DATE << ")" << endl
51 : << "Copyright (C) 2005-2008 Red Hat, Inc. and others" << endl
52 13 : << "This is free software; see the source for copying conditions." << endl;
53 13 : }
54 :
55 : void
56 9 : usage (systemtap_session& s, int exitcode)
57 : {
58 9 : version ();
59 : clog
60 : << endl
61 : << "Usage: stap [options] FILE Run script in file."
62 : << endl
63 : << " or: stap [options] - Run script on stdin."
64 : << endl
65 : << " or: stap [options] -e SCRIPT Run given script."
66 : << endl
67 : << endl
68 : << "Options:" << endl
69 : << " -- no more options after this" << endl
70 : << " -v increase verbosity [" << s.verbose << "]" << endl
71 : << " -h show help" << endl
72 : << " -V show version" << endl
73 : << " -k keep temporary directory" << endl
74 : << " -u unoptimized translation" << (s.unoptimized ? " [set]" : "") << endl
75 : << " -w suppress warnings" << (s.suppress_warnings ? " [set]" : "") << endl
76 : << " -g guru mode" << (s.guru_mode ? " [set]" : "") << endl
77 : << " -P prologue-searching for function probes"
78 : << (s.prologue_searching ? " [set]" : "") << endl
79 : << " -b bulk (percpu file) mode" << (s.bulk_mode ? " [set]" : "") << endl
80 : << " -s NUM buffer size in megabytes, instead of "
81 : << s.buffer_size << endl
82 : << " -p NUM stop after pass NUM 1-5, instead of "
83 : << s.last_pass << endl
84 : << " (parse, elaborate, translate, compile, run)" << endl
85 9 : << " -I DIR look in DIR for additional .stp script files";
86 9 : if (s.include_path.size() == 0)
87 0 : clog << endl;
88 : else
89 9 : clog << ", in addition to" << endl;
90 18 : for (unsigned i=0; i<s.include_path.size(); i++)
91 9 : clog << " " << s.include_path[i] << endl;
92 : clog
93 : << " -D NM=VAL emit macro definition into generated C code" << endl
94 : << " -R DIR look in DIR for runtime, instead of" << endl
95 : << " " << s.runtime_path << endl
96 : << " -r RELEASE cross-compile to kernel RELEASE, instead of "
97 : << s.kernel_release << endl
98 : << " -m MODULE set probe module name, instead of "
99 : << s.module_name << endl
100 : << " -o FILE send output to file, instead of stdout" << endl
101 : << " -c CMD start the probes, run CMD, and exit when it finishes"
102 : << endl
103 : << " -x PID sets target() to PID" << endl
104 : << " -t collect probe timing information" << endl
105 : #ifdef HAVE_LIBSQLITE3
106 : << " -q generate information on tapset coverage"
107 : #endif /* HAVE_LIBSQLITE3 */
108 : << endl
109 9 : ;
110 : // -d: dump safety-related external references
111 :
112 9 : exit (exitcode);
113 : }
114 :
115 :
116 : static void
117 391 : printscript(systemtap_session& s, ostream& o)
118 : {
119 391 : if (s.embeds.size() > 0)
120 119 : o << "# global embedded code" << endl;
121 1488 : for (unsigned i=0; i<s.embeds.size(); i++)
122 : {
123 1097 : embeddedcode* ec = s.embeds[i];
124 1097 : ec->print (o);
125 1097 : o << endl;
126 : }
127 :
128 391 : if (s.globals.size() > 0)
129 195 : o << "# globals" << endl;
130 1065 : for (unsigned i=0; i<s.globals.size(); i++)
131 : {
132 674 : vardecl* v = s.globals[i];
133 674 : v->printsig (o);
134 674 : if (s.verbose && v->init)
135 : {
136 73 : o << " = ";
137 73 : v->init->print(o);
138 : }
139 674 : o << endl;
140 : }
141 :
142 391 : if (s.functions.size() > 0)
143 289 : o << "# functions" << endl;
144 8033 : for (unsigned i=0; i<s.functions.size(); i++)
145 : {
146 7642 : functiondecl* f = s.functions[i];
147 7642 : f->printsig (o);
148 7642 : o << endl;
149 7642 : if (f->locals.size() > 0)
150 626 : o << " # locals" << endl;
151 8289 : for (unsigned j=0; j<f->locals.size(); j++)
152 : {
153 647 : vardecl* v = f->locals[j];
154 647 : o << " ";
155 647 : v->printsig (o);
156 647 : o << endl;
157 : }
158 7642 : if (s.verbose)
159 : {
160 7628 : f->body->print (o);
161 7628 : o << endl;
162 : }
163 : }
164 :
165 391 : if (s.probes.size() > 0)
166 391 : o << "# probes" << endl;
167 488917 : for (unsigned i=0; i<s.probes.size(); i++)
168 : {
169 488526 : derived_probe* p = s.probes[i];
170 488526 : p->printsig (o);
171 488526 : o << endl;
172 488526 : if (p->locals.size() > 0)
173 46887 : o << " # locals" << endl;
174 570047 : for (unsigned j=0; j<p->locals.size(); j++)
175 : {
176 81521 : vardecl* v = p->locals[j];
177 81521 : o << " ";
178 81521 : v->printsig (o);
179 81521 : o << endl;
180 : }
181 488526 : if (s.verbose)
182 : {
183 52800 : p->body->print (o);
184 52800 : o << endl;
185 : }
186 : }
187 391 : }
188 :
189 :
190 : int pending_interrupts;
191 :
192 : extern "C"
193 39 : void handle_interrupt (int /* sig */)
194 : {
195 39 : pending_interrupts ++;
196 39 : if (pending_interrupts > 1) // XXX: should be configurable? time-based?
197 : {
198 0 : char msg[] = "Too many interrupts received, exiting.\n";
199 0 : int rc = write (2, msg, sizeof(msg)-1);
200 : if (rc) {/* Do nothing; we don't care if our last gasp went out. */ ;}
201 0 : _exit (1);
202 : }
203 39 : }
204 :
205 :
206 : int
207 1094 : main (int argc, char * const argv [])
208 : {
209 1094 : string cmdline_script; // -e PROGRAM
210 1094 : string script_file; // FILE
211 1094 : bool have_script = false;
212 1094 : bool release_changed = false;
213 1094 : bool save_module = false;
214 :
215 : // Initialize defaults
216 1094 : systemtap_session s;
217 : struct utsname buf;
218 1094 : (void) uname (& buf);
219 1094 : s.kernel_release = string (buf.release);
220 2188 : s.architecture = string (buf.machine);
221 1094 : s.verbose = 0;
222 1094 : s.timing = false;
223 1094 : s.guru_mode = false;
224 1094 : s.bulk_mode = false;
225 1094 : s.unoptimized = false;
226 1094 : s.suppress_warnings = false;
227 :
228 : #ifdef ENABLE_PROLOGUES
229 : s.prologue_searching = true;
230 : #else
231 1094 : s.prologue_searching = false;
232 : #endif
233 :
234 1094 : s.buffer_size = 0;
235 1094 : s.last_pass = 5;
236 2188 : s.module_name = "stap_" + stringify(getpid());
237 1094 : s.output_file = ""; // -o FILE
238 1094 : s.keep_tmpdir = false;
239 1094 : s.cmd = "";
240 1094 : s.target_pid = 0;
241 1094 : s.merge=true;
242 1094 : s.perfmon=0;
243 1094 : s.symtab = false;
244 1094 : s.use_cache = true;
245 1094 : s.tapset_compile_coverage = false;
246 1094 : s.need_uprobes = false;
247 :
248 1094 : const char* s_p = getenv ("SYSTEMTAP_TAPSET");
249 1094 : if (s_p != NULL)
250 : {
251 1094 : s.include_path.push_back (s_p);
252 : }
253 : else
254 : {
255 0 : s.include_path.push_back (string(PKGDATADIR) + "/tapset");
256 : }
257 :
258 1094 : const char* s_r = getenv ("SYSTEMTAP_RUNTIME");
259 1094 : if (s_r != NULL)
260 1094 : s.runtime_path = s_r;
261 : else
262 0 : s.runtime_path = string(PKGDATADIR) + "/runtime";
263 :
264 1094 : const char* s_d = getenv ("SYSTEMTAP_DIR");
265 1094 : if (s_d != NULL)
266 1094 : s.data_path = s_d;
267 : else
268 0 : s.data_path = get_home_directory() + string("/.systemtap");
269 1094 : if (create_dir(s.data_path.c_str()) == 1)
270 : {
271 2 : const char* e = strerror (errno);
272 : cerr << "Warning: failed to create systemtap data directory (\""
273 2 : << s.data_path << "\"): " << e << endl;
274 2 : cerr << "Disabling cache support." << endl;
275 2 : s.use_cache = false;
276 : }
277 :
278 1094 : if (s.use_cache)
279 : {
280 1092 : s.cache_path = s.data_path + "/cache";
281 1092 : if (create_dir(s.cache_path.c_str()) == 1)
282 : {
283 0 : const char* e = strerror (errno);
284 : cerr << "Warning: failed to create cache directory (\""
285 0 : << s.cache_path << "\"): " << e << endl;
286 0 : cerr << "Disabling cache support." << endl;
287 0 : s.use_cache = false;
288 : }
289 : }
290 :
291 1094 : const char* s_tc = getenv ("SYSTEMTAP_COVERAGE");
292 1094 : if (s_tc != NULL)
293 0 : s.tapset_compile_coverage = true;
294 :
295 1233 : while (true)
296 : {
297 : // NB: also see find_hash(), usage(), switch stmt below, stap.1 man page
298 2327 : int grc = getopt (argc, argv, "hVMvtp:I:e:o:R:r:m:kgPc:x:D:bs:uqw");
299 2327 : if (grc < 0)
300 1086 : break;
301 1241 : switch (grc)
302 : {
303 : case 'V':
304 2 : version ();
305 2 : exit (0);
306 :
307 : case 'M':
308 4 : s.merge = false;
309 4 : break;
310 :
311 : case 'v':
312 57 : s.verbose ++;
313 57 : break;
314 :
315 : case 't':
316 2 : s.timing = true;
317 2 : break;
318 :
319 : case 'w':
320 0 : s.suppress_warnings = true;
321 0 : break;
322 :
323 : case 'p':
324 886 : s.last_pass = atoi (optarg);
325 886 : if (s.last_pass < 1 || s.last_pass > 5)
326 : {
327 1 : cerr << "Invalid pass number (should be 1-5)." << endl;
328 1 : usage (s, 1);
329 : }
330 885 : break;
331 :
332 : case 'I':
333 2 : s.include_path.push_back (string (optarg));
334 2 : break;
335 :
336 : case 'e':
337 71 : if (have_script)
338 : {
339 : cerr << "Only one script can be given on the command line."
340 1 : << endl;
341 1 : usage (s, 1);
342 : }
343 70 : cmdline_script = string (optarg);
344 70 : have_script = true;
345 70 : break;
346 :
347 : case 'o':
348 11 : s.output_file = string (optarg);
349 11 : break;
350 :
351 : case 'R':
352 2 : s.runtime_path = string (optarg);
353 2 : break;
354 :
355 : case 'm':
356 7 : s.module_name = string (optarg);
357 7 : save_module = true;
358 : {
359 14 : string::size_type len = s.module_name.length();
360 :
361 : // If the module name ends with '.ko', chop it off since
362 : // modutils doesn't like modules named 'foo.ko.ko'.
363 7 : if (len > 3 && s.module_name.substr(len - 3, 3) == ".ko")
364 : {
365 0 : s.module_name.erase(len - 3);
366 0 : len -= 3;
367 : cerr << "Truncating module name to '" << s.module_name
368 0 : << "'" << endl;
369 : }
370 :
371 : // Make sure an empty module name wasn't specified (-m "")
372 7 : if (len == 0)
373 : {
374 1 : cerr << "Module name cannot be empty." << endl;
375 1 : usage (s, 1);
376 : }
377 :
378 : // Make sure the module name is only composed of the
379 : // following chars: [_a-zA-Z0-9]
380 : const string identchars("_" "abcdefghijklmnopqrstuvwxyz"
381 6 : "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789");
382 12 : if (s.module_name.find_first_not_of(identchars) != string::npos)
383 : {
384 : cerr << "Invalid module name (must only be composed of"
385 1 : " characters [_a-zA-Z0-9])." << endl;
386 1 : usage (s, 1);
387 : }
388 :
389 : // Make sure module name isn't too long.
390 5 : if (s.module_name.size() >= (MODULE_NAME_LEN - 1))
391 : {
392 0 : s.module_name.resize(MODULE_NAME_LEN - 1);
393 : cerr << "Truncating module name to '" << s.module_name
394 0 : << "'" << endl;
395 5 : }
396 : }
397 :
398 5 : cerr << "Warning: using '-m' disables cache support." << endl;
399 5 : s.use_cache = false;
400 5 : break;
401 :
402 : case 'r':
403 0 : s.kernel_release = string (optarg);
404 0 : release_changed = true;
405 0 : break;
406 :
407 : case 'k':
408 1 : s.keep_tmpdir = true;
409 1 : break;
410 :
411 : case 'g':
412 36 : s.guru_mode = true;
413 36 : break;
414 :
415 : case 'P':
416 1 : s.prologue_searching = true;
417 1 : break;
418 :
419 : case 'b':
420 10 : s.bulk_mode = true;
421 10 : break;
422 :
423 : case 'u':
424 34 : s.unoptimized = true;
425 34 : break;
426 :
427 : case 's':
428 1 : s.buffer_size = atoi (optarg);
429 1 : if (s.buffer_size < 1 || s.buffer_size > 64)
430 : {
431 1 : cerr << "Invalid buffer size (should be 1-64)." << endl;
432 1 : usage (s, 1);
433 : }
434 0 : break;
435 :
436 : case 'c':
437 69 : s.cmd = string (optarg);
438 69 : break;
439 :
440 : case 'x':
441 2 : s.target_pid = atoi(optarg);
442 2 : break;
443 :
444 : case 'D':
445 42 : s.macros.push_back (string (optarg));
446 42 : break;
447 :
448 : case 'q':
449 0 : s.tapset_compile_coverage = true;
450 0 : break;
451 :
452 : case 'h':
453 1 : usage (s, 0);
454 0 : break;
455 :
456 : default:
457 0 : usage (s, 1);
458 : break;
459 : }
460 : }
461 :
462 1086 : if(!s.bulk_mode && !s.merge)
463 : {
464 1 : cerr << "-M option is valid only for bulk (relayfs) mode." <<endl;
465 1 : usage (s, 1);
466 : }
467 :
468 1085 : if(!s.output_file.empty() && s.bulk_mode && !s.merge)
469 : {
470 1 : cerr << "You can't specify -M, -b and -o options together." <<endl;
471 1 : usage (s, 1);
472 : }
473 :
474 1084 : if((s.cmd != "") && (s.target_pid))
475 : {
476 1 : cerr << "You can't specify -c and -x options together." <<endl;
477 1 : usage (s, 1);
478 : }
479 :
480 1083 : if (s.last_pass > 4 && release_changed)
481 : {
482 0 : if (s.verbose)
483 0 : cerr << "Warning: changing last pass to 4 since cross-compiling" << endl;
484 0 : s.last_pass = 4;
485 : }
486 :
487 2116 : for (int i = optind; i < argc; i++)
488 : {
489 1033 : if (! have_script)
490 : {
491 1017 : script_file = string (argv[i]);
492 1017 : have_script = true;
493 : }
494 : else
495 16 : s.args.push_back (string (argv[i]));
496 : }
497 :
498 : // need a user file
499 1083 : if (! have_script)
500 : {
501 0 : cerr << "A script must be specified." << endl;
502 0 : usage(s, 1);
503 : }
504 :
505 1083 : int rc = 0;
506 :
507 : // override PATH and LC_ALL
508 1083 : const char *path = "/bin:/sbin:/usr/bin:/usr/sbin";
509 1083 : rc = setenv("PATH", path, 1) || setenv("LC_ALL", "C", 1);
510 1083 : if (rc)
511 : {
512 0 : const char* e = strerror (errno);
513 : cerr << "setenv (\"PATH=" << path << "\" + \"LC_ALL=C\"): "
514 0 : << e << endl;
515 : }
516 :
517 : // Get rid of a few standard environment variables (which might
518 : // cause us to do unintended things).
519 : rc = unsetenv("IFS") || unsetenv("CDPATH") || unsetenv("ENV")
520 1083 : || unsetenv("BASH_ENV");
521 1083 : if (rc)
522 : {
523 0 : const char* e = strerror (errno);
524 0 : cerr << "unsetenv failed: " << e << endl;
525 : }
526 :
527 1083 : s.kernel_base_release.assign(s.kernel_release, 0, s.kernel_release.find('-'));
528 :
529 : // arguments parsed; get down to business
530 1083 : if (s.verbose > 1)
531 2 : version ();
532 :
533 : // Create a temporary directory to build within.
534 : // Be careful with this, as "s.tmpdir" is "rm -rf"'d at the end.
535 : {
536 1083 : const char* tmpdir_env = getenv("TMPDIR");
537 1083 : if (! tmpdir_env)
538 1082 : tmpdir_env = "/tmp";
539 :
540 1083 : string stapdir = "/stapXXXXXX";
541 1083 : string tmpdirt = tmpdir_env + stapdir;
542 1083 : mode_t mask = umask(0);
543 1083 : const char* tmpdir = mkdtemp((char *)tmpdirt.c_str());
544 1083 : umask(mask);
545 1083 : if (! tmpdir)
546 : {
547 1 : const char* e = strerror (errno);
548 1 : cerr << "ERROR: cannot create temporary directory (\"" << tmpdirt << "\"): " << e << endl;
549 1 : exit (1); // die
550 : }
551 : else
552 1082 : s.tmpdir = tmpdir;
553 :
554 1082 : if (s.verbose>1)
555 2 : clog << "Created temporary directory \"" << s.tmpdir << "\"" << endl;
556 : }
557 :
558 : // Create the name of the C source file within the temporary
559 : // directory.
560 1082 : s.translated_source = string(s.tmpdir) + "/" + s.module_name + ".c";
561 :
562 : // Set up our handler to catch routine signals, to allow clean
563 : // and reasonably timely exit.
564 1082 : signal (SIGHUP, handle_interrupt);
565 1082 : signal (SIGPIPE, handle_interrupt);
566 1082 : signal (SIGINT, handle_interrupt);
567 1082 : signal (SIGTERM, handle_interrupt);
568 :
569 : struct tms tms_before;
570 1082 : times (& tms_before);
571 : struct timeval tv_before;
572 1082 : gettimeofday (&tv_before, NULL);
573 :
574 : // PASS 1a: PARSING USER SCRIPT
575 :
576 : struct stat user_file_stat;
577 1082 : int user_file_stat_rc = -1;
578 :
579 1082 : if (script_file == "-")
580 : {
581 221 : s.user_file = parser::parse (s, cin, s.guru_mode);
582 221 : user_file_stat_rc = fstat (STDIN_FILENO, & user_file_stat);
583 : }
584 861 : else if (script_file != "")
585 : {
586 796 : s.user_file = parser::parse (s, script_file, s.guru_mode);
587 796 : user_file_stat_rc = stat (script_file.c_str(), & user_file_stat);
588 : }
589 : else
590 : {
591 65 : istringstream ii (cmdline_script);
592 65 : s.user_file = parser::parse (s, ii, s.guru_mode);
593 : }
594 1082 : if (s.user_file == 0)
595 : // syntax errors already printed
596 80 : rc ++;
597 :
598 : // Construct arch / kernel-versioning search path
599 1082 : vector<string> version_suffixes;
600 1082 : string kvr = s.kernel_release;
601 1082 : const string& arch = s.architecture;
602 : // add full kernel-version-release (2.6.NN-FOOBAR) + arch
603 1082 : version_suffixes.push_back ("/" + kvr + "/" + arch);
604 1082 : version_suffixes.push_back ("/" + kvr);
605 : // add kernel version (2.6.NN) + arch
606 1082 : if (kvr != s.kernel_base_release) {
607 1082 : kvr = s.kernel_base_release;
608 1082 : version_suffixes.push_back ("/" + kvr + "/" + arch);
609 1082 : version_suffixes.push_back ("/" + kvr);
610 : }
611 : // add kernel family (2.6) + arch
612 1082 : string::size_type dot1_index = kvr.find ('.');
613 1082 : string::size_type dot2_index = kvr.rfind ('.');
614 3246 : while (dot2_index > dot1_index && dot2_index != string::npos) {
615 1082 : kvr.erase(dot2_index);
616 1082 : version_suffixes.push_back ("/" + kvr + "/" + arch);
617 1082 : version_suffixes.push_back ("/" + kvr);
618 1082 : dot2_index = kvr.rfind ('.');
619 : }
620 : // add architecture search path
621 1082 : version_suffixes.push_back("/" + arch);
622 : // add empty string as last element
623 1082 : version_suffixes.push_back ("");
624 :
625 : // PASS 1b: PARSING LIBRARY SCRIPTS
626 2166 : for (unsigned i=0; i<s.include_path.size(); i++)
627 : {
628 : // now iterate upon it
629 9756 : for (unsigned k=0; k<version_suffixes.size(); k++)
630 : {
631 : glob_t globbuf;
632 8672 : string dir = s.include_path[i] + version_suffixes[k] + "/*.stp";
633 8672 : int r = glob(dir.c_str (), 0, NULL, & globbuf);
634 8672 : if (r == GLOB_NOSPACE || r == GLOB_ABORTED)
635 0 : rc ++;
636 : // GLOB_NOMATCH is acceptable
637 :
638 8672 : if (s.verbose>1 && globbuf.gl_pathc > 0)
639 : clog << "Searched '" << dir << "', "
640 4 : << "found " << globbuf.gl_pathc << endl;
641 :
642 49794 : for (unsigned j=0; j<globbuf.gl_pathc; j++)
643 : {
644 41122 : if (pending_interrupts)
645 0 : break;
646 :
647 : // XXX: privilege only for /usr/share/systemtap?
648 41122 : stapfile* f = parser::parse (s, globbuf.gl_pathv[j], true);
649 41122 : if (f == 0)
650 0 : rc ++;
651 : else
652 41122 : s.library_files.push_back (f);
653 :
654 : struct stat tapset_file_stat;
655 41122 : int stat_rc = stat (globbuf.gl_pathv[j], & tapset_file_stat);
656 41122 : if (stat_rc == 0 && user_file_stat_rc == 0 &&
657 : user_file_stat.st_dev == tapset_file_stat.st_dev &&
658 : user_file_stat.st_ino == tapset_file_stat.st_ino)
659 : {
660 : clog << "usage error: tapset file '" << globbuf.gl_pathv[j]
661 1 : << "' cannot be run directly as a session script." << endl;
662 1 : rc ++;
663 : }
664 :
665 : }
666 :
667 8672 : globfree (& globbuf);
668 : }
669 : }
670 :
671 1082 : if (rc == 0 && s.last_pass == 1)
672 : {
673 516 : cout << "# parse tree dump" << endl;
674 516 : s.user_file->print (cout);
675 516 : cout << endl;
676 516 : if (s.verbose)
677 39 : for (unsigned i=0; i<s.library_files.size(); i++)
678 : {
679 38 : s.library_files[i]->print (cout);
680 38 : cout << endl;
681 : }
682 : }
683 :
684 : struct tms tms_after;
685 1082 : times (& tms_after);
686 1082 : unsigned _sc_clk_tck = sysconf (_SC_CLK_TCK);
687 : struct timeval tv_after;
688 1082 : gettimeofday (&tv_after, NULL);
689 :
690 : #define TIMESPRINT \
691 : (tms_after.tms_cutime + tms_after.tms_utime \
692 : - tms_before.tms_cutime - tms_before.tms_utime) * 1000 / (_sc_clk_tck) << "usr/" \
693 : << (tms_after.tms_cstime + tms_after.tms_stime \
694 : - tms_before.tms_cstime - tms_before.tms_stime) * 1000 / (_sc_clk_tck) << "sys/" \
695 : << ((tv_after.tv_sec - tv_before.tv_sec) * 1000 + \
696 : ((long)tv_after.tv_usec - (long)tv_before.tv_usec) / 1000) << "real ms."
697 :
698 : // syntax errors, if any, are already printed
699 1082 : if (s.verbose)
700 : {
701 : clog << "Pass 1: parsed user script and "
702 : << s.library_files.size()
703 : << " library script(s) in "
704 : << TIMESPRINT
705 55 : << endl;
706 : }
707 :
708 1082 : if (rc)
709 : cerr << "Pass 1: parse failed. "
710 : << "Try again with more '-v' (verbose) options."
711 81 : << endl;
712 :
713 1082 : if (rc || s.last_pass == 1 || pending_interrupts) goto cleanup;
714 :
715 485 : times (& tms_before);
716 485 : gettimeofday (&tv_before, NULL);
717 :
718 : // PASS 2: ELABORATION
719 485 : rc = semantic_pass (s);
720 :
721 485 : if (rc == 0 && s.last_pass == 2)
722 44 : printscript(s, cout);
723 :
724 485 : times (& tms_after);
725 485 : gettimeofday (&tv_after, NULL);
726 :
727 485 : if (s.verbose) clog << "Pass 2: analyzed script: "
728 : << s.probes.size() << " probe(s), "
729 : << s.functions.size() << " function(s), "
730 : << s.embeds.size() << " embed(s), "
731 : << s.globals.size() << " global(s) in "
732 : << TIMESPRINT
733 54 : << endl;
734 :
735 485 : if (rc)
736 : cerr << "Pass 2: analysis failed. "
737 : << "Try again with more '-v' (verbose) options."
738 87 : << endl;
739 : // Generate hash. There isn't any point in generating the hash
740 : // if last_pass is 2, since we'll quit before using it.
741 398 : else if (s.last_pass != 2 && s.use_cache)
742 : {
743 347 : ostringstream o;
744 : unsigned saved_verbose;
745 :
746 : {
747 : // Make sure we're in verbose mode, so that printscript()
748 : // will output function/probe bodies.
749 347 : saved_verbose = s.verbose;
750 347 : s.verbose = 3;
751 347 : printscript(s, o); // Print script to 'o'
752 347 : s.verbose = saved_verbose;
753 : }
754 :
755 : // Generate hash
756 347 : find_hash (s, o.str());
757 :
758 : // See if we can use cached source/module.
759 347 : if (get_from_cache(s))
760 : {
761 : // If our last pass isn't 5, we're done (since passes 3 and
762 : // 4 just generate what we just pulled out of the cache).
763 77 : if (s.last_pass < 5 || pending_interrupts) goto cleanup;
764 :
765 : // Short-circuit to pass 5.
766 65 : goto pass_5;
767 0 : }
768 : }
769 :
770 414 : if (rc || s.last_pass == 2 || pending_interrupts) goto cleanup;
771 :
772 : // PASS 3: TRANSLATION
773 :
774 283 : times (& tms_before);
775 283 : gettimeofday (&tv_before, NULL);
776 :
777 283 : rc = translate_pass (s);
778 :
779 283 : if (rc == 0 && s.last_pass == 3)
780 : {
781 10 : ifstream i (s.translated_source.c_str());
782 10 : cout << i.rdbuf();
783 : }
784 :
785 283 : times (& tms_after);
786 283 : gettimeofday (&tv_after, NULL);
787 :
788 283 : if (s.verbose) clog << "Pass 3: translated to C into \""
789 : << s.translated_source
790 : << "\" in "
791 : << TIMESPRINT
792 41 : << endl;
793 :
794 283 : if (rc)
795 : cerr << "Pass 3: translation failed. "
796 : << "Try again with more '-v' (verbose) options."
797 3 : << endl;
798 :
799 283 : if (rc || s.last_pass == 3 || pending_interrupts) goto cleanup;
800 :
801 : // PASS 4: COMPILATION
802 270 : times (& tms_before);
803 270 : gettimeofday (&tv_before, NULL);
804 270 : rc = compile_pass (s);
805 :
806 270 : if (rc == 0 && s.last_pass == 4)
807 129 : cout << s.hash_path << endl;
808 :
809 270 : times (& tms_after);
810 270 : gettimeofday (&tv_after, NULL);
811 :
812 270 : if (s.verbose) clog << "Pass 4: compiled C into \""
813 : << s.module_name << ".ko"
814 : << "\" in "
815 : << TIMESPRINT
816 41 : << endl;
817 :
818 270 : if (rc)
819 : cerr << "Pass 4: compilation failed. "
820 : << "Try again with more '-v' (verbose) options."
821 2 : << endl;
822 : else
823 : {
824 : // Update cache.
825 268 : if (s.use_cache)
826 261 : add_to_cache(s);
827 :
828 : // Copy module to the current directory.
829 268 : if (save_module && !pending_interrupts)
830 : {
831 5 : string module_src_path = s.tmpdir + "/" + s.module_name + ".ko";
832 5 : string module_dest_path = s.module_name + ".ko";
833 :
834 5 : if (s.verbose > 1)
835 : clog << "Copying " << module_src_path << " to "
836 0 : << module_dest_path << endl;
837 5 : if (copy_file(module_src_path.c_str(), module_dest_path.c_str()) != 0)
838 : cerr << "Copy failed (\"" << module_src_path << "\" to \""
839 0 : << module_dest_path << "\"): " << strerror(errno) << endl;
840 : }
841 : }
842 :
843 270 : if (rc || s.last_pass == 4 || pending_interrupts) goto cleanup;
844 :
845 :
846 : // PASS 5: RUN
847 204 : pass_5:
848 204 : times (& tms_before);
849 204 : gettimeofday (&tv_before, NULL);
850 : // NB: this message is a judgement call. The other passes don't emit
851 : // a "hello, I'm starting" message, but then the others aren't interactive
852 : // and don't take an indefinite amount of time.
853 204 : if (s.verbose) clog << "Pass 5: starting run." << endl;
854 204 : rc = run_pass (s);
855 204 : times (& tms_after);
856 204 : gettimeofday (&tv_after, NULL);
857 204 : if (s.verbose) clog << "Pass 5: run completed in "
858 : << TIMESPRINT
859 31 : << endl;
860 :
861 204 : if (rc)
862 : cerr << "Pass 5: run failed. "
863 : << "Try again with more '-v' (verbose) options."
864 1 : << endl;
865 :
866 : // if (rc) goto cleanup;
867 :
868 1082 : cleanup:
869 :
870 : // update the database information
871 1082 : if (!rc && s.tapset_compile_coverage && !pending_interrupts) {
872 : #ifdef HAVE_LIBSQLITE3
873 0 : update_coverage_db(s);
874 : #else
875 : cerr << "Coverage database not available without libsqlite3" << endl;
876 : #endif
877 : }
878 :
879 : // Clean up temporary directory. Obviously, be careful with this.
880 1082 : if (s.tmpdir == "")
881 : ; // do nothing
882 : else
883 : {
884 1082 : if (s.keep_tmpdir)
885 1 : clog << "Keeping temporary directory \"" << s.tmpdir << "\"" << endl;
886 : else
887 : {
888 1081 : string cleanupcmd = "rm -rf ";
889 1081 : cleanupcmd += s.tmpdir;
890 1081 : if (s.verbose>1) clog << "Running " << cleanupcmd << endl;
891 1081 : int status = system (cleanupcmd.c_str());
892 1081 : if (status != 0 && s.verbose>1)
893 0 : clog << "Cleanup command failed, status: " << status << endl;
894 : }
895 : }
896 :
897 1082 : return (rc||pending_interrupts) ? EXIT_FAILURE : EXIT_SUCCESS;
898 2188 : }
|