]> sourceware.org Git - systemtap.git/blame - stapdyn/mutator.cxx
update copyrights
[systemtap.git] / stapdyn / mutator.cxx
CommitLineData
fce2d171 1// stapdyn mutator functions
ef36f781 2// Copyright (C) 2012-2014 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
13extern "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 25extern "C" {
fce2d171 26#include "../runtime/dyninst/stapdyn.h"
6af47bf8 27}
fce2d171
JS
28
29using 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.
34static vector<mutator*> g_mutators;
fce2d171 35
7f7eca30
JS
36static void
37g_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 46static void
f31a77f5
DS
47g_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
54static void
55g_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
62static void
63g_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 70static void
f31a77f5
DS
71g_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 78static void
f31a77f5
DS
79g_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
86static pthread_t g_main_thread = pthread_self();
87static const sigset_t *g_signal_mask;
88
4ef3a23e
JS
89static void
90g_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))
105static void
106setup_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
134mutator::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
148mutator::~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?
166bool
167mutator::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.
182bool
183mutator::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
228bool
229mutator::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.
277bool
278mutator::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
307bool
308mutator::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:
5e2950a9 316 stapwarn() << "Compiled module does not support -G globals" << endl;
bd268288
SM
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 {
5e2950a9 330 stapwarn() << "Could not parse module option '" << modoption << "'" << endl;
bd268288
SM
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 {
5e2950a9 339 stapwarn() << "Incorrect module option '" << modoption << "'" << endl;
bd268288
SM
340 return false; // XXX: perhaps ignore the option instead?
341 }
342 }
343
344 return true;
345}
346
ac033e87
DS
347void
348mutator::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)
5e2950a9 365 stapwarn() << "Couldn't set 'log_level' global" << endl;
ac033e87
DS
366
367 rc = global_setter("@suppress_warnings",
368 lex_cast(stapdyn_suppress_warnings).c_str());
369 if (rc != 0)
5e2950a9 370 stapwarn() << "Couldn't set 'suppress_warnings' global" << endl;
ac033e87 371
76fb7222
JS
372 rc = global_setter("@stp_pid", lex_cast(getpid()).c_str());
373 if (rc != 0)
5e2950a9 374 stapwarn() << "Couldn't set 'stp_pid' global" << endl;
76fb7222
JS
375
376 if (target_mutatee)
377 {
378 rc = global_setter("@target", lex_cast(target_mutatee->process_id()).c_str());
379 if (rc != 0)
5e2950a9 380 stapwarn() << "Couldn't set 'target' global" << endl;
76fb7222
JS
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)
5e2950a9 391 stapwarn() << "Couldn't set 'module_name' global" << endl;
93000d60 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)
5e2950a9 399 stapwarn() << "Couldn't set 'tz_gmtoff' global" << endl;
b8d7372e
JS
400 rc = global_setter("@tz_name", now->tm_zone);
401 if (rc != 0)
5e2950a9 402 stapwarn() << "Couldn't set 'tz_name' global" << endl;
b8d7372e
JS
403 }
404 else
5e2950a9 405 stapwarn() << "Couldn't discover local timezone info" << endl;
b8d7372e 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)
5e2950a9 412 stapwarn() << "Couldn't set 'outfile_name' global" << endl;
6d842dc7
DS
413 }
414
ac033e87
DS
415 return;
416}
417
4ef3a23e 418// Initialize the module session
fce2d171 419bool
4ef3a23e 420mutator::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
489bool
490mutator::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
524bool
525mutator::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!
553bool
554mutator::run ()
555{
315f5db3
JS
556 if (!targets.empty() && !target_mutatee)
557 stapwarn() << "process probes require a target (-c or -x)" << endl;
fce2d171
JS
558
559 // Get the stap module ready...
4ef3a23e 560 run_module_init();
fce2d171
JS
561
562 // And away we go!
4ef3a23e 563 if (target_mutatee)
72100304
JS
564 {
565 // For our first event, fire the target's process.begin probes (if any)
566 target_mutatee->begin_callback();
6d27dcff 567 target_mutatee->continue_execution();
72100304 568
180177da
JS
569 // Dyninst's notification FD was fixed in 8.1; for earlier versions we'll
570 // fall back to the fully-blocking wait for now.
571#ifdef DYNINST_8_1
4ef3a23e 572 // mask signals while we're preparing to poll
6d27dcff 573 stap_sigmasker masked(g_signal_mask);
4ef3a23e
JS
574
575 // Polling with a notification FD lets us wait on Dyninst while still
576 // letting signals break us out of the loop.
577 while (update_mutatees())
578 {
df3d071c
NMA
579 pollfd pfd;
580 pfd.fd = patch.getNotificationFD();
581 pfd.events = POLLIN;
582 pfd.revents = 0;
4ef3a23e 583
40646bea
FCE
584 struct timespec timeout = { 10, 0 };
585
586 int rc = ppoll (&pfd, 1, &timeout, &masked.old);
4ef3a23e
JS
587 if (rc < 0 && errno != EINTR)
588 break;
589
590 // Acknowledge and activate whatever events are waiting
591 patch.pollForStatusChange();
592 }
3f95ed01 593#else
4ef3a23e
JS
594 while (update_mutatees())
595 patch.waitForStatusChange();
3f95ed01 596#endif
4ef3a23e
JS
597 }
598 else // !target_mutatee
599 {
63f4648b 600 // With no mutatees, we just wait for a signal to exit.
3ee990b1
JS
601 stap_sigmasker masked(g_signal_mask);
602 while (sigisemptyset(&signals_received))
603 sigsuspend(&masked.old);
4ef3a23e 604 }
fce2d171 605
6d27dcff 606 // Indicate failure if the target had anything but EXIT_SUCCESS
6d27dcff 607 if (target_mutatee && target_mutatee->is_terminated())
e280dd54 608 p_target_error = !target_mutatee->check_exit();
6d27dcff
JS
609
610 // Detach from everything
611 target_mutatee.reset();
612 mutatees.clear();
613
4ef3a23e 614 // Shutdown the stap module.
e280dd54
JS
615 return run_module_exit();
616}
617
618
619// Get the final exit status of this mutator
620int mutator::exit_status ()
621{
622 if (!module)
623 return EXIT_FAILURE;
624
625 // NB: Only shm modules are new enough to have stp_dyninst_exit_status at
626 // all, so we don't need to try in-target for old modules like session_exit.
627
628 typeof(&stp_dyninst_exit_status) get_exit_status = NULL;
629 set_dlsym(get_exit_status, module, "stp_dyninst_exit_status", false);
630 if (get_exit_status)
631 {
632 int status = get_exit_status();
633 if (status != EXIT_SUCCESS)
634 return status;
635 }
fce2d171 636
e280dd54 637 return p_target_error ? EXIT_FAILURE : EXIT_SUCCESS;
fce2d171
JS
638}
639
640
1925d293
JS
641// Find a mutatee which matches the given process, else return NULL
642boost::shared_ptr<mutatee>
643mutator::find_mutatee(BPatch_process* process)
644{
645 for (size_t i = 0; i < mutatees.size(); ++i)
646 if (*mutatees[i] == process)
647 return mutatees[i];
648 return boost::shared_ptr<mutatee>();
649}
650
651
7f7eca30
JS
652// Callback to respond to dynamically loaded libraries.
653// Check if it matches our targets, and instrument accordingly.
654void
655mutator::dynamic_library_callback(BPatch_thread *thread,
656 BPatch_module *module,
657 bool load)
fce2d171 658{
7f7eca30
JS
659 if (!load || !thread || !module)
660 return;
fce2d171 661
7f7eca30 662 BPatch_process* process = thread->getProcess();
b9babf95
JS
663 staplog(1) << "dlopen \"" << module->libraryName()
664 << "\", pid = " << process->getPid() << endl;
1925d293
JS
665 boost::shared_ptr<mutatee> mut = find_mutatee(process);
666 if (mut)
667 mut->instrument_object_dynprobes(module->getObject(), targets);
fce2d171
JS
668}
669
7f7eca30 670
f31a77f5
DS
671// Callback to respond to post fork events. Check if it matches our
672// targets, and handle accordingly.
673void
674mutator::post_fork_callback(BPatch_thread *parent, BPatch_thread *child)
675{
676 if (!child || !parent)
677 return;
678
679 BPatch_process* child_process = child->getProcess();
680 BPatch_process* parent_process = parent->getProcess();
681
682 staplog(1) << "post fork, parent " << parent_process->getPid()
683 << ", child " << child_process->getPid() << endl;
684
72100304
JS
685 boost::shared_ptr<mutatee> mut = find_mutatee(parent_process);
686 if (mut)
687 {
688 // Clone the mutatee for the new process.
a235a8f6 689 boost::shared_ptr<mutatee> m(new mutatee(child_process));
72100304
JS
690 mutatees.push_back(m);
691 m->copy_forked_instrumentation(*mut);
692
693 // Trigger any process.begin probes.
90b7e794 694 m->begin_callback(child);
72100304 695 }
f31a77f5
DS
696}
697
698
b9babf95
JS
699// Callback to respond to exec events. Check if it matches our
700// targets, and handle accordingly.
701void
702mutator::exec_callback(BPatch_thread *thread)
703{
704 if (!thread)
705 return;
706
707 BPatch_process* process = thread->getProcess();
708
709 staplog(1) << "exec, pid = " << process->getPid() << endl;
710
711 boost::shared_ptr<mutatee> mut = find_mutatee(process);
712 if (mut)
713 {
714 // Clear previous instrumentation
715 mut->exec_reset_instrumentation();
716
b8a251b9
JS
717 // NB: Until Dyninst commit 2b6c10ac15dc (in 8.2), loadLibrary in a
718 // fork-execed would hang waiting for a stopped process to continue.
719#ifdef DYNINST_8_2
b9babf95
JS
720 // Load our module again in the new process
721 if (mut->load_stap_dso(module_name))
b8a251b9
JS
722 {
723 if (!targets.empty())
90b7e794 724 mut->instrument_dynprobes(targets);
b8a251b9
JS
725
726 // Now we map the shared-memory into the target
727 if (!module_shmem.empty())
728 {
729 vector<BPatch_snippet *> args;
730 args.push_back(new BPatch_constExpr(module_shmem.c_str()));
731 mut->call_function("stp_dyninst_shm_connect", args);
732 }
733
90b7e794
JS
734 // Trigger any process.end probes for the pre-exec process.
735 mut->exit_callback(thread, true);
736
b8a251b9 737 // Trigger any process.begin probes.
90b7e794 738 mut->begin_callback(thread);
b8a251b9 739 }
b9babf95
JS
740#endif
741 }
742}
743
744
f31a77f5 745void
1925d293 746mutator::exit_callback(BPatch_thread *thread,
f31a77f5
DS
747 BPatch_exitType type __attribute__((unused)))
748{
1925d293 749 if (!thread)
f31a77f5
DS
750 return;
751
1925d293 752 // 'thread' is the thread that requested the exit, not necessarily the
f31a77f5 753 // main thread.
1925d293 754 BPatch_process* process = thread->getProcess();
90b7e794
JS
755 int pid = process->getPid();
756 staplog(1) << "exit callback, pid = " << pid << endl;
1925d293
JS
757
758 boost::shared_ptr<mutatee> mut = find_mutatee(process);
759 if (mut)
03399f75
DS
760 {
761 // FIXME: We'd like to call the mutatee's exit_callback()
762 // function, but we've got a problem. The mutatee can't stop the
763 // process to call the exit probe within the target (it finishes
764 // exiting before we can). So, we'll call the probe(s) locally
765 // here. This works, but the context is wrong (the mutator, not
766 // the mutatee).
90b7e794
JS
767 const vector<dynprobe_location>& proc_end_probes =
768 mut->find_attached_probes(STAPDYN_PROBE_FLAG_PROC_END);
769 if (proc_end_probes.empty())
770 return;
771
772 if (utrace_enter_fn == NULL)
773 try
774 {
775 set_dlsym(utrace_enter_fn, module, "enter_dyninst_utrace_probe");
776 }
777 catch (runtime_error& e)
778 {
779 staperror() << e.what() << endl;
780 return;
781 }
782
783 staplog(2) << "firing " << proc_end_probes.size()
784 << " process.end probes in the mutator for pid "
785 << pid << endl;
786 for (size_t p = 0; p < proc_end_probes.size(); ++p)
03399f75 787 {
90b7e794
JS
788 const dynprobe_location& probe = proc_end_probes[p];
789 staplog(3) << "calling utrace function in the mutator for pid "
790 << pid << ", probe index " << probe.index << endl;
791 int rc = utrace_enter_fn(probe.index, NULL);
03399f75
DS
792 if (rc)
793 stapwarn() << "enter_dyninst_utrace_probe returned "
794 << rc << endl;
795 }
796 }
f31a77f5
DS
797}
798
799
800void
801mutator::thread_create_callback(BPatch_process *proc, BPatch_thread *thread)
802{
803 if (!proc || !thread)
804 return;
805
1925d293
JS
806 boost::shared_ptr<mutatee> mut = find_mutatee(proc);
807 if (mut)
808 mut->thread_callback(thread, true);
f31a77f5
DS
809}
810
811
812void
813mutator::thread_destroy_callback(BPatch_process *proc, BPatch_thread *thread)
814{
815 if (!proc || !thread)
816 return;
817
1925d293
JS
818 boost::shared_ptr<mutatee> mut = find_mutatee(proc);
819 if (mut)
820 mut->thread_callback(thread, false);
f31a77f5
DS
821}
822
823
4ef3a23e
JS
824// Callback to respond to signals.
825void
6d27dcff 826mutator::signal_callback(int signal)
4ef3a23e 827{
6d27dcff 828 sigaddset(&signals_received, signal);
4ef3a23e
JS
829}
830
831
fce2d171 832/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.150575 seconds and 5 git commands to generate.