]> sourceware.org Git - systemtap.git/blame - stapdyn/mutator.cxx
examples: change eventcount.* demo to report every 10 seconds by default
[systemtap.git] / stapdyn / mutator.cxx
CommitLineData
fce2d171
JS
1// stapdyn mutator functions
2// Copyright (C) 2012 Red Hat Inc.
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>
fce2d171
JS
17}
18
19#include "dynutil.h"
20#include "../util.h"
21
22#include "../runtime/dyninst/stapdyn.h"
23
24
25using namespace std;
26
27
7f7eca30
JS
28// NB: since Dyninst callbacks have no context, we have to demux it
29// to every mutator we've created, tracked by this vector.
30static vector<mutator*> g_mutators;
fce2d171 31
7f7eca30
JS
32static void
33g_dynamic_library_callback(BPatch_thread *thread,
34 BPatch_module *module,
35 bool load)
fce2d171 36{
7f7eca30
JS
37 for (size_t i = 0; i < g_mutators.size(); ++i)
38 g_mutators[i]->dynamic_library_callback(thread, module, load);
fce2d171
JS
39}
40
4ef3a23e
JS
41static void
42g_signal_handler(int signal)
43{
44 for (size_t i = 0; i < g_mutators.size(); ++i)
45 g_mutators[i]->signal_callback(signal);
46}
47
48
49__attribute__((constructor))
50static void
51setup_signals (void)
52{
53 struct sigaction sa;
54 static const int signals[] = {
55 SIGHUP, SIGPIPE, SIGINT, SIGTERM,
56 };
57
58 memset(&sa, 0, sizeof(sa));
59 sa.sa_handler = g_signal_handler;
60 sa.sa_flags = SA_RESTART;
61 sigemptyset (&sa.sa_mask);
62 for (size_t i = 0; i < sizeof(signals) / sizeof(*signals); ++i)
63 sigaddset (&sa.sa_mask, signals[i]);
64 for (size_t i = 0; i < sizeof(signals) / sizeof(*signals); ++i)
65 sigaction (signals[i], &sa, NULL);
66}
fce2d171 67
4ef3a23e
JS
68
69mutator:: mutator (const string& module_name):
70 module(NULL), module_name(resolve_path(module_name)),
71 p_target_created(false), signal_count(0)
fce2d171
JS
72{
73 // NB: dlopen does a library-path search if the filename doesn't have any
74 // path components, which is why we use resolve_path(module_name)
7f7eca30
JS
75
76 g_mutators.push_back(this);
fce2d171
JS
77}
78
79mutator::~mutator ()
80{
4ef3a23e
JS
81 // Explicitly drop our mutatee references, so we better
82 // control when their instrumentation is removed.
83 target_mutatee.reset();
848dec37
JS
84 mutatees.clear();
85
fce2d171
JS
86 if (module)
87 {
88 dlclose(module);
89 module = NULL;
90 }
7f7eca30
JS
91
92 g_mutators.erase(find(g_mutators.begin(), g_mutators.end(), this));
fce2d171
JS
93}
94
95// Load the stap module and initialize all probe info.
96bool
97mutator::load ()
98{
99 int rc;
100
101 // Open the module directly, so we can query probes or run simple ones.
102 (void)dlerror(); // clear previous errors
103 module = dlopen(module_name.c_str(), RTLD_NOW);
104 if (!module)
105 {
106 staperror() << "dlopen " << dlerror() << endl;
107 return false;
108 }
109
110 if ((rc = find_dynprobes(module, targets)))
111 return rc;
112 if (!targets.empty())
7f7eca30 113 patch.registerDynLibraryCallback(g_dynamic_library_callback);
fce2d171
JS
114
115 return true;
116}
117
118// Create a new process with the given command line
119bool
120mutator::create_process(const string& command)
121{
4ef3a23e
JS
122 if (target_mutatee)
123 {
124 staperror() << "Already attached to a target process!" << endl;
125 return false;
126 }
127
fce2d171
JS
128 // Split the command into words. If wordexp can't do it,
129 // we'll just run via "sh -c" instead.
130 const char** child_argv;
131 const char* sh_argv[] = { "/bin/sh", "-c", command.c_str(), NULL };
132 wordexp_t words;
133 int rc = wordexp (command.c_str(), &words, WRDE_NOCMD|WRDE_UNDEF);
134 if (rc == 0)
135 child_argv = (/*cheater*/ const char**) words.we_wordv;
136 else if (rc == WRDE_BADCHAR)
137 child_argv = sh_argv;
138 else
139 {
140 staperror() << "wordexp parsing error (" << rc << ")" << endl;
141 return false;
142 }
143
144 // Search the PATH if necessary, then create the target process!
145 string fullpath = find_executable(child_argv[0]);
146 BPatch_process* app = patch.processCreate(fullpath.c_str(), child_argv);
147 if (!app)
148 {
149 staperror() << "Couldn't create the target process" << endl;
150 return false;
151 }
152
848dec37
JS
153 boost::shared_ptr<mutatee> m(new mutatee(app));
154 mutatees.push_back(m);
4ef3a23e
JS
155 target_mutatee = m;
156 p_target_created = true;
848dec37
JS
157
158 if (!m->load_stap_dso(module_name))
fce2d171
JS
159 return false;
160
161 if (!targets.empty())
848dec37 162 m->instrument_dynprobes(targets);
fce2d171
JS
163
164 return true;
165}
166
18e3d52a
JS
167// Attach to a specific existing process.
168bool
169mutator::attach_process(pid_t pid)
170{
171 if (target_mutatee)
172 {
173 staperror() << "Already attached to a target process!" << endl;
174 return false;
175 }
176
177 BPatch_process* app = patch.processAttach(NULL, pid);
178 if (!app)
179 {
180 staperror() << "Couldn't attach to the target process" << endl;
181 return false;
182 }
183
184 boost::shared_ptr<mutatee> m(new mutatee(app));
185 mutatees.push_back(m);
186 target_mutatee = m;
187 p_target_created = false;
188
189 if (!m->load_stap_dso(module_name))
190 return false;
191
192 if (!targets.empty())
193 m->instrument_dynprobes(targets);
194
195 return true;
196}
197
4ef3a23e 198// Initialize the module session
fce2d171 199bool
4ef3a23e 200mutator::run_module_init()
fce2d171 201{
4ef3a23e
JS
202 // When we get multiple mutatees, we'll need to always do the basic
203 // begin/end/timers locally, but for now we'll run init/exit in the
204 // target if we have one.
205 if (target_mutatee)
206 {
207 target_mutatee->call_function("stp_dyninst_session_init");
208 return true;
209 }
210
fce2d171
JS
211 if (!module)
212 return false;
213
214 typeof(&stp_dyninst_session_init) session_init = NULL;
fce2d171
JS
215 try
216 {
217 set_dlsym(session_init, module, "stp_dyninst_session_init");
fce2d171
JS
218 }
219 catch (runtime_error& e)
220 {
221 staperror() << e.what() << endl;
222 return false;
223 }
224
225 int rc = session_init();
226 if (rc)
227 {
228 stapwarn() << "stp_dyninst_session_init returned " << rc << endl;
229 return false;
230 }
231
4ef3a23e
JS
232 return true;
233}
234
235// Shutdown the module session
236bool
237mutator::run_module_exit()
238{
239 // When we get multiple mutatees, we'll need to always do the basic
240 // begin/end/timers locally, but for now we'll run init/exit in the
241 // target if we have one.
242 // XXX This may already have been done in its deconstructor if the process exited.
243 if (target_mutatee)
244 {
245 target_mutatee->call_function("stp_dyninst_session_exit");
246 return true;
247 }
248
249 if (!module)
250 return false;
251
252 typeof(&stp_dyninst_session_exit) session_exit = NULL;
253 try
254 {
255 set_dlsym(session_exit, module, "stp_dyninst_session_exit");
256 }
257 catch (runtime_error& e)
258 {
259 staperror() << e.what() << endl;
260 return false;
261 }
fce2d171
JS
262
263 session_exit();
4ef3a23e
JS
264 return true;
265}
266
267
268// Check the status of all mutatees
269bool
270mutator::update_mutatees()
271{
272 if (signal_count >= (p_target_created ? 2 : 1))
273 return false;
274
275 if (target_mutatee && target_mutatee->is_terminated())
276 return false;
277
278 for (size_t i = 0; i < mutatees.size();)
279 {
280 boost::shared_ptr<mutatee> m = mutatees[i];
281 if (m != target_mutatee && m->is_terminated())
282 {
283 mutatees.erase(mutatees.begin() + i);
284 continue; // NB: without ++i
285 }
286
287 if (m->is_stopped())
288 m->continue_execution();
289
290 ++i;
291 }
fce2d171
JS
292
293 return true;
294}
295
296
297// Start the actual systemtap session!
298bool
299mutator::run ()
300{
fce2d171
JS
301
302 // Get the stap module ready...
4ef3a23e 303 run_module_init();
fce2d171
JS
304
305 // And away we go!
4ef3a23e
JS
306 if (target_mutatee)
307 {
308 // XXX Dyninst's notification FD is currently broken, so we'll fall back
309 // to the fully-blocking wait for now.
3f95ed01 310#if 0
4ef3a23e
JS
311 // mask signals while we're preparing to poll
312 stap_sigmasker masked;
313
314 // Polling with a notification FD lets us wait on Dyninst while still
315 // letting signals break us out of the loop.
316 while (update_mutatees())
317 {
318 pollfd pfd = { .fd=patch.getNotificationFD(),
319 .events=POLLIN, .revents=0 };
320
321 int rc = ppoll (&pfd, 1, NULL, &masked.old);
322 if (rc < 0 && errno != EINTR)
323 break;
324
325 // Acknowledge and activate whatever events are waiting
326 patch.pollForStatusChange();
327 }
3f95ed01 328#else
4ef3a23e
JS
329 while (update_mutatees())
330 patch.waitForStatusChange();
3f95ed01 331#endif
4ef3a23e
JS
332 }
333 else // !target_mutatee
334 {
335 // XXX TODO we really ought to wait for a signal before exiting,
336 // or for a script requested exit (e.g. from a timer probe).
337 }
fce2d171 338
4ef3a23e
JS
339 // Shutdown the stap module.
340 run_module_exit();
fce2d171 341
4ef3a23e 342 return target_mutatee ? target_mutatee->check_exit() : true;
fce2d171
JS
343}
344
345
7f7eca30
JS
346// Callback to respond to dynamically loaded libraries.
347// Check if it matches our targets, and instrument accordingly.
348void
349mutator::dynamic_library_callback(BPatch_thread *thread,
350 BPatch_module *module,
351 bool load)
fce2d171 352{
7f7eca30
JS
353 if (!load || !thread || !module)
354 return;
fce2d171 355
7f7eca30
JS
356 BPatch_process* process = thread->getProcess();
357
358 for (size_t i = 0; i < mutatees.size(); ++i)
848dec37
JS
359 if (*mutatees[i] == process)
360 mutatees[i]->instrument_object_dynprobes(module->getObject(), targets);
fce2d171
JS
361}
362
7f7eca30 363
4ef3a23e
JS
364// Callback to respond to signals.
365void
366mutator::signal_callback(int signal __attribute__((unused)))
367{
368 ++signal_count;
369
370 // First time, try to kill the target process, only if we created it.
371 if (signal_count == 1 && target_mutatee && p_target_created)
372 target_mutatee->kill(SIGTERM);
373
374 // Second time, mutator::run should break out anyway
375 if (signal_count == 2)
376 stapwarn() << "Multiple interrupts received, exiting..." << endl;
377
378 // Third time's the charm; the user wants OUT!
379 if (signal_count >= 3)
380 {
381 staperror() << "Too many interrupts received, aborting now!" << endl;
382 _exit (1);
383 }
384}
385
386
fce2d171 387/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.178891 seconds and 5 git commands to generate.