]>
Commit | Line | Data |
---|---|---|
fce2d171 | 1 | // stapdyn mutator functions |
f31a77f5 | 2 | // Copyright (C) 2012-2013 Red Hat Inc. |
fce2d171 JS |
3 | // |
4 | // This file is part of systemtap, and is free software. You can | |
5 | // redistribute it and/or modify it under the terms of the GNU General | |
6 | // Public License (GPL); either version 2, or (at your option) any | |
7 | // later version. | |
8 | ||
9 | #include "mutator.h" | |
10 | ||
7f7eca30 JS |
11 | #include <algorithm> |
12 | ||
fce2d171 JS |
13 | extern "C" { |
14 | #include <dlfcn.h> | |
15 | #include <wordexp.h> | |
4ef3a23e | 16 | #include <signal.h> |
b8d7372e | 17 | #include <time.h> |
fce2d171 JS |
18 | } |
19 | ||
f4d70a33 JS |
20 | #include <BPatch_snippet.h> |
21 | ||
fce2d171 JS |
22 | #include "dynutil.h" |
23 | #include "../util.h" | |
24 | ||
6af47bf8 | 25 | extern "C" { |
fce2d171 | 26 | #include "../runtime/dyninst/stapdyn.h" |
6af47bf8 | 27 | } |
fce2d171 JS |
28 | |
29 | using namespace std; | |
30 | ||
31 | ||
7f7eca30 JS |
32 | // NB: since Dyninst callbacks have no context, we have to demux it |
33 | // to every mutator we've created, tracked by this vector. | |
34 | static vector<mutator*> g_mutators; | |
fce2d171 | 35 | |
7f7eca30 JS |
36 | static void |
37 | g_dynamic_library_callback(BPatch_thread *thread, | |
38 | BPatch_module *module, | |
39 | bool load) | |
fce2d171 | 40 | { |
7f7eca30 JS |
41 | for (size_t i = 0; i < g_mutators.size(); ++i) |
42 | g_mutators[i]->dynamic_library_callback(thread, module, load); | |
fce2d171 JS |
43 | } |
44 | ||
f31a77f5 | 45 | |
1925d293 | 46 | static void |
f31a77f5 DS |
47 | g_post_fork_callback(BPatch_thread *parent, BPatch_thread *child) |
48 | { | |
49 | for (size_t i = 0; i < g_mutators.size(); ++i) | |
50 | g_mutators[i]->post_fork_callback(parent, child); | |
51 | } | |
52 | ||
53 | ||
b9babf95 JS |
54 | static void |
55 | g_exec_callback(BPatch_thread *thread) | |
56 | { | |
57 | for (size_t i = 0; i < g_mutators.size(); ++i) | |
58 | g_mutators[i]->exec_callback(thread); | |
59 | } | |
60 | ||
61 | ||
1925d293 JS |
62 | static void |
63 | g_exit_callback(BPatch_thread *thread, BPatch_exitType type) | |
f31a77f5 DS |
64 | { |
65 | for (size_t i = 0; i < g_mutators.size(); ++i) | |
1925d293 | 66 | g_mutators[i]->exit_callback(thread, type); |
f31a77f5 DS |
67 | } |
68 | ||
69 | ||
1925d293 | 70 | static void |
f31a77f5 DS |
71 | g_thread_create_callback(BPatch_process *proc, BPatch_thread *thread) |
72 | { | |
73 | for (size_t i = 0; i < g_mutators.size(); ++i) | |
74 | g_mutators[i]->thread_create_callback(proc, thread); | |
75 | } | |
76 | ||
77 | ||
1925d293 | 78 | static void |
f31a77f5 DS |
79 | g_thread_destroy_callback(BPatch_process *proc, BPatch_thread *thread) |
80 | { | |
81 | for (size_t i = 0; i < g_mutators.size(); ++i) | |
82 | g_mutators[i]->thread_destroy_callback(proc, thread); | |
83 | } | |
84 | ||
85 | ||
6d27dcff JS |
86 | static pthread_t g_main_thread = pthread_self(); |
87 | static const sigset_t *g_signal_mask; | |
88 | ||
4ef3a23e JS |
89 | static void |
90 | g_signal_handler(int signal) | |
91 | { | |
6d27dcff JS |
92 | /* We only want the signal on our main thread, so it will interrupt the ppoll |
93 | * loop. If we get it on a different thread, just forward it. */ | |
94 | if (!pthread_equal(pthread_self(), g_main_thread)) | |
95 | { | |
96 | pthread_kill(g_main_thread, signal); | |
97 | return; | |
98 | } | |
99 | ||
4ef3a23e JS |
100 | for (size_t i = 0; i < g_mutators.size(); ++i) |
101 | g_mutators[i]->signal_callback(signal); | |
102 | } | |
103 | ||
4ef3a23e JS |
104 | __attribute__((constructor)) |
105 | static void | |
106 | setup_signals (void) | |
107 | { | |
108 | struct sigaction sa; | |
6d27dcff | 109 | static sigset_t mask; |
4ef3a23e | 110 | static const int signals[] = { |
6d27dcff | 111 | SIGHUP, SIGINT, SIGTERM, SIGQUIT, |
4ef3a23e JS |
112 | }; |
113 | ||
6d27dcff JS |
114 | /* Prepare the global sigmask for future use. */ |
115 | sigemptyset (&mask); | |
116 | for (size_t i = 0; i < sizeof(signals) / sizeof(*signals); ++i) | |
117 | sigaddset (&mask, signals[i]); | |
118 | g_signal_mask = &mask; | |
119 | ||
120 | /* Prepare the common signal handler. */ | |
4ef3a23e JS |
121 | memset(&sa, 0, sizeof(sa)); |
122 | sa.sa_handler = g_signal_handler; | |
123 | sa.sa_flags = SA_RESTART; | |
124 | sigemptyset (&sa.sa_mask); | |
125 | for (size_t i = 0; i < sizeof(signals) / sizeof(*signals); ++i) | |
126 | sigaddset (&sa.sa_mask, signals[i]); | |
6d27dcff JS |
127 | |
128 | /* Activate the handler for every signal. */ | |
4ef3a23e JS |
129 | for (size_t i = 0; i < sizeof(signals) / sizeof(*signals); ++i) |
130 | sigaction (signals[i], &sa, NULL); | |
131 | } | |
fce2d171 | 132 | |
4ef3a23e | 133 | |
e280dd54 JS |
134 | mutator::mutator (const string& module_name, |
135 | vector<string>& module_options): | |
4ef3a23e | 136 | module(NULL), module_name(resolve_path(module_name)), |
bd268288 | 137 | modoptions(module_options), p_target_created(false), |
e280dd54 | 138 | p_target_error(false), utrace_enter_fn(NULL) |
fce2d171 JS |
139 | { |
140 | // NB: dlopen does a library-path search if the filename doesn't have any | |
141 | // path components, which is why we use resolve_path(module_name) | |
7f7eca30 | 142 | |
6d27dcff JS |
143 | sigemptyset(&signals_received); |
144 | ||
7f7eca30 | 145 | g_mutators.push_back(this); |
fce2d171 JS |
146 | } |
147 | ||
148 | mutator::~mutator () | |
149 | { | |
4ef3a23e JS |
150 | // Explicitly drop our mutatee references, so we better |
151 | // control when their instrumentation is removed. | |
152 | target_mutatee.reset(); | |
848dec37 JS |
153 | mutatees.clear(); |
154 | ||
fce2d171 JS |
155 | if (module) |
156 | { | |
157 | dlclose(module); | |
158 | module = NULL; | |
159 | } | |
7f7eca30 JS |
160 | |
161 | g_mutators.erase(find(g_mutators.begin(), g_mutators.end(), this)); | |
fce2d171 JS |
162 | } |
163 | ||
f31a77f5 DS |
164 | |
165 | // Do probes matching 'flag' exist? | |
166 | bool | |
167 | mutator::matching_probes_exist(uint64_t flag) | |
168 | { | |
169 | for (size_t i = 0; i < targets.size(); ++i) | |
170 | { | |
171 | for (size_t j = 0; j < targets[i].probes.size(); ++j) | |
172 | { | |
173 | if (targets[i].probes[j].flags & flag) | |
174 | return true; | |
175 | } | |
176 | } | |
177 | return false; | |
178 | } | |
179 | ||
180 | ||
fce2d171 JS |
181 | // Load the stap module and initialize all probe info. |
182 | bool | |
183 | mutator::load () | |
184 | { | |
185 | int rc; | |
186 | ||
187 | // Open the module directly, so we can query probes or run simple ones. | |
188 | (void)dlerror(); // clear previous errors | |
189 | module = dlopen(module_name.c_str(), RTLD_NOW); | |
190 | if (!module) | |
191 | { | |
192 | staperror() << "dlopen " << dlerror() << endl; | |
193 | return false; | |
194 | } | |
195 | ||
196 | if ((rc = find_dynprobes(module, targets))) | |
197 | return rc; | |
198 | if (!targets.empty()) | |
f31a77f5 | 199 | { |
964519ac | 200 | // Always watch for new libraries to probe. |
f31a77f5 DS |
201 | patch.registerDynLibraryCallback(g_dynamic_library_callback); |
202 | ||
964519ac JS |
203 | // Always watch for new child processes, even if we don't have |
204 | // STAPDYN_PROBE_FLAG_PROC_BEGIN, because we might want to trigger | |
205 | // any of the other types of probes in new processes too. | |
206 | patch.registerPostForkCallback(g_post_fork_callback); | |
b9babf95 | 207 | patch.registerExecCallback(g_exec_callback); |
f31a77f5 DS |
208 | |
209 | // Do we need a exit callback? | |
210 | if (matching_probes_exist(STAPDYN_PROBE_FLAG_PROC_END)) | |
211 | patch.registerExitCallback(g_exit_callback); | |
212 | ||
213 | // Do we need a thread create callback? | |
214 | if (matching_probes_exist(STAPDYN_PROBE_FLAG_THREAD_BEGIN)) | |
215 | patch.registerThreadEventCallback(BPatch_threadCreateEvent, | |
216 | g_thread_create_callback); | |
217 | ||
218 | // Do we need a thread destroy callback? | |
219 | if (matching_probes_exist(STAPDYN_PROBE_FLAG_THREAD_END)) | |
220 | patch.registerThreadEventCallback(BPatch_threadDestroyEvent, | |
221 | g_thread_destroy_callback); | |
222 | } | |
fce2d171 JS |
223 | |
224 | return true; | |
225 | } | |
226 | ||
227 | // Create a new process with the given command line | |
228 | bool | |
229 | mutator::create_process(const string& command) | |
230 | { | |
4ef3a23e JS |
231 | if (target_mutatee) |
232 | { | |
233 | staperror() << "Already attached to a target process!" << endl; | |
234 | return false; | |
235 | } | |
236 | ||
fce2d171 JS |
237 | // Split the command into words. If wordexp can't do it, |
238 | // we'll just run via "sh -c" instead. | |
239 | const char** child_argv; | |
240 | const char* sh_argv[] = { "/bin/sh", "-c", command.c_str(), NULL }; | |
241 | wordexp_t words; | |
242 | int rc = wordexp (command.c_str(), &words, WRDE_NOCMD|WRDE_UNDEF); | |
243 | if (rc == 0) | |
244 | child_argv = (/*cheater*/ const char**) words.we_wordv; | |
245 | else if (rc == WRDE_BADCHAR) | |
246 | child_argv = sh_argv; | |
247 | else | |
248 | { | |
249 | staperror() << "wordexp parsing error (" << rc << ")" << endl; | |
250 | return false; | |
251 | } | |
252 | ||
253 | // Search the PATH if necessary, then create the target process! | |
254 | string fullpath = find_executable(child_argv[0]); | |
255 | BPatch_process* app = patch.processCreate(fullpath.c_str(), child_argv); | |
256 | if (!app) | |
257 | { | |
258 | staperror() << "Couldn't create the target process" << endl; | |
259 | return false; | |
260 | } | |
261 | ||
a235a8f6 | 262 | boost::shared_ptr<mutatee> m(new mutatee(app)); |
848dec37 | 263 | mutatees.push_back(m); |
4ef3a23e JS |
264 | target_mutatee = m; |
265 | p_target_created = true; | |
848dec37 JS |
266 | |
267 | if (!m->load_stap_dso(module_name)) | |
fce2d171 JS |
268 | return false; |
269 | ||
270 | if (!targets.empty()) | |
848dec37 | 271 | m->instrument_dynprobes(targets); |
fce2d171 JS |
272 | |
273 | return true; | |
274 | } | |
275 | ||
18e3d52a JS |
276 | // Attach to a specific existing process. |
277 | bool | |
278 | mutator::attach_process(pid_t pid) | |
279 | { | |
280 | if (target_mutatee) | |
281 | { | |
282 | staperror() << "Already attached to a target process!" << endl; | |
283 | return false; | |
284 | } | |
285 | ||
286 | BPatch_process* app = patch.processAttach(NULL, pid); | |
287 | if (!app) | |
288 | { | |
289 | staperror() << "Couldn't attach to the target process" << endl; | |
290 | return false; | |
291 | } | |
292 | ||
a235a8f6 | 293 | boost::shared_ptr<mutatee> m(new mutatee(app)); |
18e3d52a JS |
294 | mutatees.push_back(m); |
295 | target_mutatee = m; | |
296 | p_target_created = false; | |
297 | ||
298 | if (!m->load_stap_dso(module_name)) | |
299 | return false; | |
300 | ||
301 | if (!targets.empty()) | |
302 | m->instrument_dynprobes(targets); | |
303 | ||
304 | return true; | |
305 | } | |
306 | ||
bd268288 SM |
307 | bool |
308 | mutator::init_modoptions() | |
309 | { | |
310 | typeof(&stp_global_setter) global_setter = NULL; | |
311 | set_dlsym(global_setter, module, "stp_global_setter", false); | |
312 | ||
313 | if (global_setter == NULL) | |
314 | { | |
315 | // Hypothetical backwards compatibility with older stapdyn: | |
316 | stapwarn() << "compiled module does not support -G globals" << endl; | |
317 | return false; | |
318 | } | |
319 | ||
320 | for (vector<string>::iterator it = modoptions.begin(); | |
321 | it != modoptions.end(); it++) | |
322 | { | |
323 | string modoption = *it; | |
324 | ||
325 | // Parse modoption as "name=value" | |
326 | // XXX: compare whether this behaviour fits safety regex in buildrun.cxx | |
327 | string::size_type separator = modoption.find('='); | |
328 | if (separator == string::npos) | |
329 | { | |
330 | stapwarn() << "could not parse module option '" << modoption << "'" << endl; | |
331 | return false; // XXX: perhaps ignore the option instead? | |
332 | } | |
333 | string name = modoption.substr(0, separator); | |
334 | string value = modoption.substr(separator+1); | |
335 | ||
336 | int rc = global_setter(name.c_str(), value.c_str()); | |
337 | if (rc != 0) | |
338 | { | |
339 | stapwarn() << "incorrect module option '" << modoption << "'" << endl; | |
340 | return false; // XXX: perhaps ignore the option instead? | |
341 | } | |
342 | } | |
343 | ||
344 | return true; | |
345 | } | |
346 | ||
ac033e87 DS |
347 | void |
348 | mutator::init_session_attributes() | |
349 | { | |
350 | typeof(&stp_global_setter) global_setter = NULL; | |
351 | set_dlsym(global_setter, module, "stp_global_setter", false); | |
352 | ||
353 | if (global_setter == NULL) | |
354 | { | |
355 | // Just return. | |
356 | return; | |
357 | } | |
358 | ||
359 | // Note that the list of supported attributes should match with the | |
360 | // list in 'struct _stp_sesion_attributes' in | |
361 | // runtime/dyninst/session_attributes.h. | |
362 | ||
363 | int rc = global_setter("@log_level", lex_cast(stapdyn_log_level).c_str()); | |
364 | if (rc != 0) | |
365 | stapwarn() << "couldn't set 'log_level' global" << endl; | |
366 | ||
367 | rc = global_setter("@suppress_warnings", | |
368 | lex_cast(stapdyn_suppress_warnings).c_str()); | |
369 | if (rc != 0) | |
370 | stapwarn() << "couldn't set 'suppress_warnings' global" << endl; | |
371 | ||
76fb7222 JS |
372 | rc = global_setter("@stp_pid", lex_cast(getpid()).c_str()); |
373 | if (rc != 0) | |
374 | stapwarn() << "couldn't set 'stp_pid' global" << endl; | |
375 | ||
376 | if (target_mutatee) | |
377 | { | |
378 | rc = global_setter("@target", lex_cast(target_mutatee->process_id()).c_str()); | |
379 | if (rc != 0) | |
380 | stapwarn() << "couldn't set 'target' global" << endl; | |
381 | } | |
382 | ||
93000d60 JS |
383 | size_t module_endpath = module_name.rfind('/'); |
384 | size_t module_basename_start = | |
385 | (module_endpath != string::npos) ? module_endpath + 1 : 0; | |
386 | size_t module_basename_end = module_name.find('.', module_basename_start); | |
387 | size_t module_basename_len = module_basename_end - module_basename_start; | |
388 | string module_basename(module_name, module_basename_start, module_basename_len); | |
389 | rc = global_setter("@module_name", module_basename.c_str()); | |
390 | if (rc != 0) | |
391 | stapwarn() << "couldn't set 'module_name' global" << endl; | |
392 | ||
b8d7372e JS |
393 | time_t now_t = time(NULL); |
394 | struct tm* now = localtime(&now_t); | |
395 | if (now) | |
396 | { | |
397 | rc = global_setter("@tz_gmtoff", lex_cast(-now->tm_gmtoff).c_str()); | |
398 | if (rc != 0) | |
399 | stapwarn() << "couldn't set 'tz_gmtoff' global" << endl; | |
400 | rc = global_setter("@tz_name", now->tm_zone); | |
401 | if (rc != 0) | |
402 | stapwarn() << "couldn't set 'tz_name' global" << endl; | |
403 | } | |
404 | else | |
405 | stapwarn() << "couldn't discover local timezone info" << endl; | |
406 | ||
6d842dc7 DS |
407 | if (stapdyn_outfile_name) |
408 | { | |
409 | rc = global_setter("@outfile_name", | |
410 | lex_cast(stapdyn_outfile_name).c_str()); | |
411 | if (rc != 0) | |
412 | stapwarn() << "couldn't set 'outfile_name' global" << endl; | |
413 | } | |
414 | ||
ac033e87 DS |
415 | return; |
416 | } | |
417 | ||
4ef3a23e | 418 | // Initialize the module session |
fce2d171 | 419 | bool |
4ef3a23e | 420 | mutator::run_module_init() |
fce2d171 | 421 | { |
f4d70a33 JS |
422 | if (!module) |
423 | return false; | |
424 | ||
425 | // First see if this is a shared-memory, multiprocess-capable module | |
f90fb80b DS |
426 | typeof(&stp_dyninst_shm_init) shm_init = NULL; |
427 | typeof(&stp_dyninst_shm_connect) shm_connect = NULL; | |
f4d70a33 JS |
428 | set_dlsym(shm_init, module, "stp_dyninst_shm_init", false); |
429 | set_dlsym(shm_connect, module, "stp_dyninst_shm_connect", false); | |
430 | if (shm_init && shm_connect) | |
4ef3a23e | 431 | { |
f4d70a33 JS |
432 | // Initialize the shared-memory locally. |
433 | const char* shmem = shm_init(); | |
434 | if (shmem == NULL) | |
435 | { | |
436 | stapwarn() << "stp_dyninst_shm_init failed!" << endl; | |
437 | return false; | |
438 | } | |
439 | module_shmem = shmem; | |
440 | // After the session is initilized, then we'll map shmem in the target | |
441 | } | |
442 | else if (target_mutatee) | |
443 | { | |
444 | // For modules that don't support shared-memory, but still have a target | |
445 | // process, we'll run init/exit in the target. | |
4ef3a23e JS |
446 | target_mutatee->call_function("stp_dyninst_session_init"); |
447 | return true; | |
448 | } | |
449 | ||
f4d70a33 JS |
450 | // From here, either this is a shared-memory module, |
451 | // or we have no target and thus run init directly anyway. | |
fce2d171 | 452 | |
f90fb80b | 453 | typeof(&stp_dyninst_session_init) session_init = NULL; |
fce2d171 JS |
454 | try |
455 | { | |
456 | set_dlsym(session_init, module, "stp_dyninst_session_init"); | |
fce2d171 JS |
457 | } |
458 | catch (runtime_error& e) | |
459 | { | |
460 | staperror() << e.what() << endl; | |
461 | return false; | |
462 | } | |
463 | ||
bd268288 SM |
464 | // Before init runs, set any custom variables |
465 | if (!modoptions.empty() && !init_modoptions()) | |
466 | return false; | |
467 | ||
ac033e87 DS |
468 | init_session_attributes(); |
469 | ||
fce2d171 JS |
470 | int rc = session_init(); |
471 | if (rc) | |
472 | { | |
473 | stapwarn() << "stp_dyninst_session_init returned " << rc << endl; | |
474 | return false; | |
475 | } | |
476 | ||
f4d70a33 JS |
477 | // Now we map the shared-memory into the target |
478 | if (target_mutatee && !module_shmem.empty()) | |
479 | { | |
480 | vector<BPatch_snippet *> args; | |
481 | args.push_back(new BPatch_constExpr(module_shmem.c_str())); | |
482 | target_mutatee->call_function("stp_dyninst_shm_connect", args); | |
483 | } | |
484 | ||
4ef3a23e JS |
485 | return true; |
486 | } | |
487 | ||
488 | // Shutdown the module session | |
489 | bool | |
490 | mutator::run_module_exit() | |
491 | { | |
f4d70a33 JS |
492 | if (!module) |
493 | return false; | |
494 | ||
495 | if (target_mutatee && module_shmem.empty()) | |
4ef3a23e | 496 | { |
f4d70a33 JS |
497 | // For modules that don't support shared-memory, but still have a target |
498 | // process, we'll run init/exit in the target. | |
499 | // XXX This may already have been done in its deconstructor if the process exited. | |
4ef3a23e JS |
500 | target_mutatee->call_function("stp_dyninst_session_exit"); |
501 | return true; | |
502 | } | |
503 | ||
f4d70a33 JS |
504 | // From here, either this is a shared-memory module, |
505 | // or we have no target and thus run exit directly anyway. | |
4ef3a23e | 506 | |
f90fb80b | 507 | typeof(&stp_dyninst_session_exit) session_exit = NULL; |
4ef3a23e JS |
508 | try |
509 | { | |
510 | set_dlsym(session_exit, module, "stp_dyninst_session_exit"); | |
511 | } | |
512 | catch (runtime_error& e) | |
513 | { | |
514 | staperror() << e.what() << endl; | |
515 | return false; | |
516 | } | |
fce2d171 JS |
517 | |
518 | session_exit(); | |
4ef3a23e JS |
519 | return true; |
520 | } | |
521 | ||
522 | ||
523 | // Check the status of all mutatees | |
524 | bool | |
525 | mutator::update_mutatees() | |
526 | { | |
6d27dcff JS |
527 | // We'll always break right away for SIGQUIT. We'll also break for any other |
528 | // signal if we didn't create the process. Otherwise, we should give the | |
529 | // created process a chance to finish. | |
530 | if (sigismember(&signals_received, SIGQUIT) || | |
531 | (!sigisemptyset(&signals_received) && !p_target_created)) | |
4ef3a23e JS |
532 | return false; |
533 | ||
534 | if (target_mutatee && target_mutatee->is_terminated()) | |
535 | return false; | |
536 | ||
537 | for (size_t i = 0; i < mutatees.size();) | |
538 | { | |
539 | boost::shared_ptr<mutatee> m = mutatees[i]; | |
540 | if (m != target_mutatee && m->is_terminated()) | |
541 | { | |
542 | mutatees.erase(mutatees.begin() + i); | |
543 | continue; // NB: without ++i | |
544 | } | |
4ef3a23e JS |
545 | ++i; |
546 | } | |
fce2d171 JS |
547 | |
548 | return true; | |
549 | } | |
550 | ||
551 | ||
552 | // Start the actual systemtap session! | |
553 | bool | |
554 | mutator::run () | |
555 | { | |
fce2d171 JS |
556 | |
557 | // Get the stap module ready... | |
4ef3a23e | 558 | run_module_init(); |
fce2d171 JS |
559 | |
560 | // And away we go! | |
4ef3a23e | 561 | if (target_mutatee) |
72100304 JS |
562 | { |
563 | // For our first event, fire the target's process.begin probes (if any) | |
564 | target_mutatee->begin_callback(); | |
6d27dcff | 565 | target_mutatee->continue_execution(); |
72100304 | 566 | |
180177da JS |
567 | // Dyninst's notification FD was fixed in 8.1; for earlier versions we'll |
568 | // fall back to the fully-blocking wait for now. | |
569 | #ifdef DYNINST_8_1 | |
4ef3a23e | 570 | // mask signals while we're preparing to poll |
6d27dcff | 571 | stap_sigmasker masked(g_signal_mask); |
4ef3a23e JS |
572 | |
573 | // Polling with a notification FD lets us wait on Dyninst while still | |
574 | // letting signals break us out of the loop. | |
575 | while (update_mutatees()) | |
576 | { | |
df3d071c NMA |
577 | pollfd pfd; |
578 | pfd.fd = patch.getNotificationFD(); | |
579 | pfd.events = POLLIN; | |
580 | pfd.revents = 0; | |
4ef3a23e | 581 | |
40646bea FCE |
582 | struct timespec timeout = { 10, 0 }; |
583 | ||
584 | int rc = ppoll (&pfd, 1, &timeout, &masked.old); | |
4ef3a23e JS |
585 | if (rc < 0 && errno != EINTR) |
586 | break; | |
587 | ||
588 | // Acknowledge and activate whatever events are waiting | |
589 | patch.pollForStatusChange(); | |
590 | } | |
3f95ed01 | 591 | #else |
4ef3a23e JS |
592 | while (update_mutatees()) |
593 | patch.waitForStatusChange(); | |
3f95ed01 | 594 | #endif |
4ef3a23e JS |
595 | } |
596 | else // !target_mutatee | |
597 | { | |
63f4648b | 598 | // With no mutatees, we just wait for a signal to exit. |
3ee990b1 JS |
599 | stap_sigmasker masked(g_signal_mask); |
600 | while (sigisemptyset(&signals_received)) | |
601 | sigsuspend(&masked.old); | |
4ef3a23e | 602 | } |
fce2d171 | 603 | |
6d27dcff | 604 | // Indicate failure if the target had anything but EXIT_SUCCESS |
6d27dcff | 605 | if (target_mutatee && target_mutatee->is_terminated()) |
e280dd54 | 606 | p_target_error = !target_mutatee->check_exit(); |
6d27dcff JS |
607 | |
608 | // Detach from everything | |
609 | target_mutatee.reset(); | |
610 | mutatees.clear(); | |
611 | ||
4ef3a23e | 612 | // Shutdown the stap module. |
e280dd54 JS |
613 | return run_module_exit(); |
614 | } | |
615 | ||
616 | ||
617 | // Get the final exit status of this mutator | |
618 | int mutator::exit_status () | |
619 | { | |
620 | if (!module) | |
621 | return EXIT_FAILURE; | |
622 | ||
623 | // NB: Only shm modules are new enough to have stp_dyninst_exit_status at | |
624 | // all, so we don't need to try in-target for old modules like session_exit. | |
625 | ||
626 | typeof(&stp_dyninst_exit_status) get_exit_status = NULL; | |
627 | set_dlsym(get_exit_status, module, "stp_dyninst_exit_status", false); | |
628 | if (get_exit_status) | |
629 | { | |
630 | int status = get_exit_status(); | |
631 | if (status != EXIT_SUCCESS) | |
632 | return status; | |
633 | } | |
fce2d171 | 634 | |
e280dd54 | 635 | return p_target_error ? EXIT_FAILURE : EXIT_SUCCESS; |
fce2d171 JS |
636 | } |
637 | ||
638 | ||
1925d293 JS |
639 | // Find a mutatee which matches the given process, else return NULL |
640 | boost::shared_ptr<mutatee> | |
641 | mutator::find_mutatee(BPatch_process* process) | |
642 | { | |
643 | for (size_t i = 0; i < mutatees.size(); ++i) | |
644 | if (*mutatees[i] == process) | |
645 | return mutatees[i]; | |
646 | return boost::shared_ptr<mutatee>(); | |
647 | } | |
648 | ||
649 | ||
7f7eca30 JS |
650 | // Callback to respond to dynamically loaded libraries. |
651 | // Check if it matches our targets, and instrument accordingly. | |
652 | void | |
653 | mutator::dynamic_library_callback(BPatch_thread *thread, | |
654 | BPatch_module *module, | |
655 | bool load) | |
fce2d171 | 656 | { |
7f7eca30 JS |
657 | if (!load || !thread || !module) |
658 | return; | |
fce2d171 | 659 | |
7f7eca30 | 660 | BPatch_process* process = thread->getProcess(); |
b9babf95 JS |
661 | staplog(1) << "dlopen \"" << module->libraryName() |
662 | << "\", pid = " << process->getPid() << endl; | |
1925d293 JS |
663 | boost::shared_ptr<mutatee> mut = find_mutatee(process); |
664 | if (mut) | |
665 | mut->instrument_object_dynprobes(module->getObject(), targets); | |
fce2d171 JS |
666 | } |
667 | ||
7f7eca30 | 668 | |
f31a77f5 DS |
669 | // Callback to respond to post fork events. Check if it matches our |
670 | // targets, and handle accordingly. | |
671 | void | |
672 | mutator::post_fork_callback(BPatch_thread *parent, BPatch_thread *child) | |
673 | { | |
674 | if (!child || !parent) | |
675 | return; | |
676 | ||
677 | BPatch_process* child_process = child->getProcess(); | |
678 | BPatch_process* parent_process = parent->getProcess(); | |
679 | ||
680 | staplog(1) << "post fork, parent " << parent_process->getPid() | |
681 | << ", child " << child_process->getPid() << endl; | |
682 | ||
72100304 JS |
683 | boost::shared_ptr<mutatee> mut = find_mutatee(parent_process); |
684 | if (mut) | |
685 | { | |
686 | // Clone the mutatee for the new process. | |
a235a8f6 | 687 | boost::shared_ptr<mutatee> m(new mutatee(child_process)); |
72100304 JS |
688 | mutatees.push_back(m); |
689 | m->copy_forked_instrumentation(*mut); | |
690 | ||
691 | // Trigger any process.begin probes. | |
692 | m->begin_callback(); | |
693 | } | |
f31a77f5 DS |
694 | } |
695 | ||
696 | ||
b9babf95 JS |
697 | // Callback to respond to exec events. Check if it matches our |
698 | // targets, and handle accordingly. | |
699 | void | |
700 | mutator::exec_callback(BPatch_thread *thread) | |
701 | { | |
702 | if (!thread) | |
703 | return; | |
704 | ||
705 | BPatch_process* process = thread->getProcess(); | |
706 | ||
707 | staplog(1) << "exec, pid = " << process->getPid() << endl; | |
708 | ||
709 | boost::shared_ptr<mutatee> mut = find_mutatee(process); | |
710 | if (mut) | |
711 | { | |
712 | // Clear previous instrumentation | |
713 | mut->exec_reset_instrumentation(); | |
714 | ||
715 | // FIXME the loadLibrary is hanging in Dyninst waiting for IRPC. | |
716 | // I've tried deferring this until update_mutatees() too - same hang. | |
717 | #if 0 | |
718 | // Load our module again in the new process | |
719 | if (mut->load_stap_dso(module_name)) | |
720 | mut->instrument_dynprobes(targets, true); | |
721 | #endif | |
722 | } | |
723 | } | |
724 | ||
725 | ||
f31a77f5 | 726 | void |
1925d293 | 727 | mutator::exit_callback(BPatch_thread *thread, |
f31a77f5 DS |
728 | BPatch_exitType type __attribute__((unused))) |
729 | { | |
1925d293 | 730 | if (!thread) |
f31a77f5 DS |
731 | return; |
732 | ||
1925d293 | 733 | // 'thread' is the thread that requested the exit, not necessarily the |
f31a77f5 | 734 | // main thread. |
1925d293 | 735 | BPatch_process* process = thread->getProcess(); |
f31a77f5 | 736 | |
03399f75 DS |
737 | if (utrace_enter_fn == NULL) |
738 | { | |
739 | try | |
740 | { | |
741 | set_dlsym(utrace_enter_fn, module, "enter_dyninst_utrace_probe"); | |
742 | } | |
743 | catch (runtime_error& e) | |
744 | { | |
745 | staperror() << e.what() << endl; | |
746 | return; | |
747 | } | |
748 | } | |
749 | ||
f31a77f5 | 750 | staplog(1) << "exit callback, pid = " << process->getPid() << endl; |
1925d293 JS |
751 | |
752 | boost::shared_ptr<mutatee> mut = find_mutatee(process); | |
753 | if (mut) | |
03399f75 DS |
754 | { |
755 | // FIXME: We'd like to call the mutatee's exit_callback() | |
756 | // function, but we've got a problem. The mutatee can't stop the | |
757 | // process to call the exit probe within the target (it finishes | |
758 | // exiting before we can). So, we'll call the probe(s) locally | |
759 | // here. This works, but the context is wrong (the mutator, not | |
760 | // the mutatee). | |
761 | vector<const dynprobe_location *> exit_probes; | |
1326c583 | 762 | mut->find_attached_probes(STAPDYN_PROBE_FLAG_PROC_END, exit_probes); |
03399f75 DS |
763 | for (size_t p = 0; p < exit_probes.size(); ++p) |
764 | { | |
765 | const dynprobe_location *probe = exit_probes[p]; | |
766 | staplog(1) << "found end proc probe, index = " << probe->index | |
767 | << endl; | |
768 | int rc = utrace_enter_fn(probe->index, NULL); | |
769 | if (rc) | |
770 | stapwarn() << "enter_dyninst_utrace_probe returned " | |
771 | << rc << endl; | |
772 | } | |
773 | } | |
f31a77f5 DS |
774 | } |
775 | ||
776 | ||
777 | void | |
778 | mutator::thread_create_callback(BPatch_process *proc, BPatch_thread *thread) | |
779 | { | |
780 | if (!proc || !thread) | |
781 | return; | |
782 | ||
1925d293 JS |
783 | boost::shared_ptr<mutatee> mut = find_mutatee(proc); |
784 | if (mut) | |
785 | mut->thread_callback(thread, true); | |
f31a77f5 DS |
786 | } |
787 | ||
788 | ||
789 | void | |
790 | mutator::thread_destroy_callback(BPatch_process *proc, BPatch_thread *thread) | |
791 | { | |
792 | if (!proc || !thread) | |
793 | return; | |
794 | ||
1925d293 JS |
795 | boost::shared_ptr<mutatee> mut = find_mutatee(proc); |
796 | if (mut) | |
797 | mut->thread_callback(thread, false); | |
f31a77f5 DS |
798 | } |
799 | ||
800 | ||
4ef3a23e JS |
801 | // Callback to respond to signals. |
802 | void | |
6d27dcff | 803 | mutator::signal_callback(int signal) |
4ef3a23e | 804 | { |
6d27dcff | 805 | sigaddset(&signals_received, signal); |
4ef3a23e JS |
806 | } |
807 | ||
808 | ||
fce2d171 | 809 | /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */ |