]>
Commit | Line | Data |
---|---|---|
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 |
13 | extern "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 | ||
25 | using 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. | |
30 | static vector<mutator*> g_mutators; | |
fce2d171 | 31 | |
7f7eca30 JS |
32 | static void |
33 | g_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 |
41 | static void |
42 | g_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)) | |
50 | static void | |
51 | setup_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 | |
69 | mutator:: 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 | ||
79 | mutator::~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. | |
96 | bool | |
97 | mutator::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 | |
119 | bool | |
120 | mutator::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. |
168 | bool | |
169 | mutator::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 | 199 | bool |
4ef3a23e | 200 | mutator::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 | |
236 | bool | |
237 | mutator::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 | |
269 | bool | |
270 | mutator::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! | |
298 | bool | |
299 | mutator::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. | |
348 | void | |
349 | mutator::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. |
365 | void | |
366 | mutator::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 : */ |