1 : // build/run probes
2 : // Copyright (C) 2005-2007 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 "config.h"
10 : #include "buildrun.h"
11 : #include "session.h"
12 : #include "util.h"
13 :
14 : #include <cstdlib>
15 : #include <fstream>
16 : #include <sstream>
17 :
18 : extern "C" {
19 : #include <signal.h>
20 : #include <sys/wait.h>
21 : #include <pwd.h>
22 : #include <sys/types.h>
23 : #include <sys/stat.h>
24 : #include <unistd.h>
25 : #include <string.h>
26 : #include <errno.h>
27 : }
28 :
29 :
30 : using namespace std;
31 :
32 : static int uprobes_pass (systemtap_session& s);
33 :
34 : /* Adjust and run make_cmd to build a kernel module. */
35 : static int
36 270 : run_make_cmd(systemtap_session& s, string& make_cmd)
37 : {
38 : // Before running make, fix up the environment a bit. PATH should
39 : // already be overridden. Clean out a few variables that
40 : // /lib/modules/${KVER}/build/Makefile uses.
41 : int rc = unsetenv("ARCH") || unsetenv("KBUILD_EXTMOD")
42 : || unsetenv("CROSS_COMPILE") || unsetenv("KBUILD_IMAGE")
43 270 : || unsetenv("KCONFIG_CONFIG") || unsetenv("INSTALL_PATH");
44 270 : if (rc)
45 : {
46 0 : const char* e = strerror (errno);
47 0 : cerr << "unsetenv failed: " << e << endl;
48 : }
49 :
50 270 : if (s.verbose > 2)
51 0 : make_cmd += " V=1";
52 270 : else if (s.verbose > 1)
53 0 : make_cmd += " >/dev/null";
54 : else
55 270 : make_cmd += " -s >/dev/null 2>&1";
56 :
57 270 : if (s.verbose > 1) clog << "Running " << make_cmd << endl;
58 270 : rc = system (make_cmd.c_str());
59 :
60 270 : return rc;
61 : }
62 :
63 : int
64 270 : compile_pass (systemtap_session& s)
65 : {
66 270 : int rc = uprobes_pass (s);
67 270 : if (rc)
68 0 : return rc;
69 :
70 : // fill in a quick Makefile
71 270 : string makefile_nm = s.tmpdir + "/Makefile";
72 270 : ofstream o (makefile_nm.c_str());
73 :
74 : // Create makefile
75 :
76 : // Clever hacks copied from vmware modules
77 270 : o << "stap_check_gcc = $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null > /dev/null 2>&1; then echo \"$(1)\"; else echo \"$(2)\"; fi)" << endl;
78 270 : o << "stap_check_build = $(shell if $(CC) $(KBUILD_CPPFLAGS) $(CPPFLAGS) $(KBUILD_CFLAGS) $(CFLAGS_KERNEL) $(EXTRA_CFLAGS) $(CFLAGS) -DKBUILD_BASENAME=\\\"" << s.module_name << "\\\" -Werror -S -o /dev/null -xc $(1) > /dev/null 2>&1 ; then echo \"$(2)\"; else echo \"$(3)\"; fi)" << endl;
79 :
80 :
81 270 : o << "SYSTEMTAP_RUNTIME = \"" << s.runtime_path << "\"" << endl;
82 :
83 : // "autoconf" options go here
84 :
85 : // enum hrtimer_mode renaming near 2.6.21; see tapsets.cxx hrtimer_derived_probe_group::emit_module_decls
86 270 : string module_cflags = "CFLAGS_" + s.module_name + ".o";
87 270 : o << module_cflags << " :=" << endl;
88 270 : o << module_cflags << " += $(call stap_check_build, $(SYSTEMTAP_RUNTIME)/autoconf-hrtimer-rel.c, -DSTAPCONF_HRTIMER_REL,)" << endl;
89 270 : o << module_cflags << " += $(call stap_check_build, $(SYSTEMTAP_RUNTIME)/autoconf-inode-private.c, -DSTAPCONF_INODE_PRIVATE,)" << endl;
90 270 : o << module_cflags << " += $(call stap_check_build, $(SYSTEMTAP_RUNTIME)/autoconf-constant-tsc.c, -DSTAPCONF_CONSTANT_TSC,)" << endl;
91 270 : o << module_cflags << " += $(call stap_check_build, $(SYSTEMTAP_RUNTIME)/autoconf-tsc-khz.c, -DSTAPCONF_TSC_KHZ,)" << endl;
92 270 : o << module_cflags << " += $(call stap_check_build, $(SYSTEMTAP_RUNTIME)/autoconf-ktime-get-real.c, -DSTAPCONF_KTIME_GET_REAL,)" << endl;
93 270 : o << module_cflags << " += $(call stap_check_build, $(SYSTEMTAP_RUNTIME)/autoconf-x86-uniregs.c, -DSTAPCONF_X86_UNIREGS,)" << endl;
94 :
95 270 : o << module_cflags << " += $(call stap_check_build, $(SYSTEMTAP_RUNTIME)/autoconf-nameidata.c, -DSTAPCONF_NAMEIDATA_CLEANUP,)" << endl;
96 :
97 311 : for (unsigned i=0; i<s.macros.size(); i++)
98 41 : o << "EXTRA_CFLAGS += -D " << lex_cast_qstring(s.macros[i]) << endl;
99 :
100 270 : if (s.verbose > 3)
101 0 : o << "EXTRA_CFLAGS += -ftime-report -Q" << endl;
102 :
103 : // XXX: unfortunately, -save-temps can't work since linux kbuild cwd
104 : // is not writeable.
105 : //
106 : // if (s.keep_tmpdir)
107 : // o << "CFLAGS += -fverbose-asm -save-temps" << endl;
108 :
109 270 : o << "EXTRA_CFLAGS += -freorder-blocks" << endl; // improve on -Os
110 :
111 : // o << "CFLAGS += -fno-unit-at-a-time" << endl;
112 :
113 : // Assumes linux 2.6 kbuild
114 270 : o << "EXTRA_CFLAGS += -Wno-unused -Werror" << endl;
115 270 : o << "EXTRA_CFLAGS += -I\"" << s.runtime_path << "\"" << endl;
116 : // XXX: this may help ppc toc overflow
117 : // o << "CFLAGS := $(subst -Os,-O2,$(CFLAGS)) -fminimal-toc" << endl;
118 270 : o << "obj-m := " << s.module_name << ".o" << endl;
119 :
120 270 : o.close ();
121 :
122 : // Generate module directory pathname and make sure it exists.
123 : string module_dir = string("/lib/modules/")
124 270 : + s.kernel_release + "/build";
125 : struct stat st;
126 540 : rc = stat(module_dir.c_str(), &st);
127 270 : if (rc != 0)
128 : {
129 : clog << "Module directory " << module_dir << " check failed: "
130 : << strerror(errno) << endl
131 0 : << "Make sure kernel devel is installed." << endl;
132 270 : return rc;
133 : }
134 :
135 : // Run make
136 : string make_cmd = string("make")
137 270 : + string (" -C \"") + module_dir + string("\"");
138 540 : make_cmd += string(" M=\"") + s.tmpdir + string("\" modules");
139 :
140 540 : rc = run_make_cmd(s, make_cmd);
141 :
142 270 : return rc;
143 : }
144 :
145 2188 : static const string uprobes_home = string(PKGDATADIR "/runtime/uprobes");
146 :
147 : /*
148 : * If uprobes was built as part of the kernel build (either built-in
149 : * or as a module), the uprobes exports should show up in
150 : * /lib/modules/`uname -r`/build/Module.symvers. Return true if so.
151 : */
152 : static bool
153 0 : kernel_built_uprobes (systemtap_session& s)
154 : {
155 : string grep_cmd = string ("/bin/grep -q unregister_uprobe /lib/modules/")
156 0 : + s.kernel_release + string ("/build/Module.symvers");
157 0 : int rc = system (grep_cmd.c_str());
158 0 : return (rc == 0);
159 : }
160 :
161 : static bool
162 0 : verify_uprobes_uptodate (systemtap_session& s)
163 : {
164 0 : if (s.verbose)
165 : clog << "Pass 4, preamble: "
166 : << "verifying that SystemTap's version of uprobes is up to date."
167 0 : << endl;
168 :
169 : string make_cmd = string("make -q -C ") + uprobes_home
170 0 : + string(" uprobes.ko");
171 0 : int rc = run_make_cmd(s, make_cmd);
172 0 : if (rc) {
173 0 : clog << "SystemTap's version of uprobes is out of date." << endl;
174 0 : clog << "As root, run \"make\" in " << uprobes_home << "." << endl;
175 : }
176 :
177 0 : return rc;
178 : }
179 :
180 : static int
181 0 : make_uprobes (systemtap_session& s)
182 : {
183 0 : if (s.verbose)
184 : clog << "Pass 4, preamble: "
185 : << "(re)building SystemTap's version of uprobes."
186 0 : << endl;
187 :
188 0 : string make_cmd = string("make -C ") + uprobes_home;
189 0 : int rc = run_make_cmd(s, make_cmd);
190 0 : if (s.verbose) {
191 0 : if (rc)
192 0 : clog << "Uprobes (re)build failed." << endl;
193 : else
194 0 : clog << "Uprobes (re)build complete." << endl;
195 : }
196 :
197 0 : return rc;
198 : }
199 :
200 : /*
201 : * Copy uprobes' exports (in Module.symvers) into the temporary directory
202 : * so the script-module build can find them.
203 : */
204 : static int
205 0 : copy_uprobes_symbols (systemtap_session& s)
206 : {
207 : string cp_cmd = string("/bin/cp ") + uprobes_home +
208 0 : string("/Module.symvers ") + s.tmpdir;
209 0 : int rc = system (cp_cmd.c_str());
210 0 : return rc;
211 : }
212 :
213 : static int
214 270 : uprobes_pass (systemtap_session& s)
215 : {
216 270 : if (!s.need_uprobes || kernel_built_uprobes(s))
217 270 : return 0;
218 : /*
219 : * We need to use the version of uprobes that comes with SystemTap, so
220 : * we may need to rebuild uprobes.ko there. Unfortunately, this is
221 : * never a no-op; e.g., the modpost step gets run every time. We don't
222 : * want non-root users modifying uprobes, so we keep the uprobes
223 : * directory writable only by root. But that means a non-root member
224 : * of group stapdev can't run the make even if everything's up to date.
225 : *
226 : * So for non-root users, we just use "make -q" with a fake target to
227 : * verify that uprobes doesn't need to be rebuilt. If that's not so,
228 : * stap must fail.
229 : */
230 : int rc;
231 0 : if (geteuid() == 0) {
232 0 : rc = make_uprobes(s);
233 0 : if (rc == 0)
234 0 : rc = copy_uprobes_symbols(s);
235 : } else
236 0 : rc = verify_uprobes_uptodate(s);
237 0 : return rc;
238 : }
239 :
240 : int
241 204 : run_pass (systemtap_session& s)
242 : {
243 204 : int rc = 0;
244 :
245 204 : struct passwd *pw = getpwuid(getuid());
246 204 : string username = string(pw->pw_name);
247 :
248 : // for now, just spawn staprun
249 : string staprun_cmd = string(BINDIR) + "/staprun "
250 : + (s.verbose>1 ? "-v " : "")
251 : + (s.verbose>2 ? "-v " : "")
252 408 : + (s.output_file.empty() ? "" : "-o " + s.output_file + " ");
253 :
254 204 : staprun_cmd += "-d " + stringify(getpid()) + " ";
255 :
256 204 : if (s.cmd != "")
257 68 : staprun_cmd += "-c " + cmdstr_quoted(s.cmd) + " ";
258 :
259 204 : if (s.target_pid)
260 0 : staprun_cmd += "-t " + stringify(s.target_pid) + " ";
261 :
262 204 : if (s.buffer_size)
263 0 : staprun_cmd += "-b " + stringify(s.buffer_size) + " ";
264 :
265 204 : if (s.need_uprobes)
266 0 : staprun_cmd += "-u ";
267 :
268 204 : staprun_cmd += s.tmpdir + "/" + s.module_name + ".ko";
269 :
270 204 : if (s.verbose>1) clog << "Running " << staprun_cmd << endl;
271 :
272 204 : rc = system (staprun_cmd.c_str ());
273 204 : return rc;
274 2188 : }
|