summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xscripts/crosstap586
1 files changed, 450 insertions, 136 deletions
diff --git a/scripts/crosstap b/scripts/crosstap
index 39739bba3a..e33fa4ad46 100755
--- a/scripts/crosstap
+++ b/scripts/crosstap
@@ -1,15 +1,22 @@
1#!/bin/bash 1#!/usr/bin/env python3
2# 2#
3# Run a systemtap script on remote target 3# Build a systemtap script for a given image, kernel
4# 4#
5# Examples (run on build host, target is 192.168.1.xxx): 5# Effectively script extracts needed information from set of
6# $ source oe-init-build-env" 6# 'bitbake -e' commands and contructs proper invocation of stap on
7# $ cd ~/my/systemtap/scripts" 7# host to build systemtap script for a given target.
8# 8#
9# $ crosstap root@192.168.1.xxx myscript.stp" 9# By default script will compile scriptname.ko that could be copied
10# $ crosstap root@192.168.1.xxx myscript-with-args.stp 99 ninetynine" 10# to taget and activated with 'staprun scriptname.ko' command. Or if
11# --remote user@hostname option is specified script will build, load
12# execute script on target.
11# 13#
12# Copyright (c) 2012, Intel Corporation. 14# This script is very similar and inspired by crosstap shell script.
15# The major difference that this script supports user-land related
16# systemtap script, whereas crosstap could deal only with scripts
17# related to kernel.
18#
19# Copyright (c) 2018, Cisco Systems.
13# All rights reserved. 20# All rights reserved.
14# 21#
15# This program is free software; you can redistribute it and/or modify 22# This program is free software; you can redistribute it and/or modify
@@ -25,131 +32,438 @@
25# along with this program; if not, write to the Free Software 32# along with this program; if not, write to the Free Software
26# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 33# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 34
28function usage() { 35import sys
29 echo "Usage: $0 <user@hostname> <sytemtap-script> [additional systemtap-script args]" 36import re
30} 37import subprocess
31 38import os
32function setup_usage() { 39import optparse
33 echo "" 40
34 echo "'crosstap' requires a local sdk build of the target system" 41class Stap(object):
35 echo "(or a build that includes 'tools-profile') in order to build" 42 def __init__(self, script, module, remote):
36 echo "kernel modules that can probe the target system." 43 self.script = script
37 echo "" 44 self.module = module
38 echo "Practically speaking, that means you need to do the following:" 45 self.remote = remote
39 echo " - If you're running a pre-built image, download the release" 46 self.stap = None
40 echo " and/or BSP tarballs used to build the image." 47 self.sysroot = None
41 echo " - If you're working from git sources, just clone the metadata" 48 self.runtime = None
42 echo " and BSP layers needed to build the image you'll be booting." 49 self.tapset = None
43 echo " - Make sure you're properly set up to build a new image (see" 50 self.arch = None
44 echo " the BSP README and/or the widely available basic documentation" 51 self.cross_compile = None
45 echo " that discusses how to build images)." 52 self.kernel_release = None
46 echo " - Build an -sdk version of the image e.g.:" 53 self.target_path = None
47 echo " $ bitbake core-image-sato-sdk" 54 self.target_ld_library_path = None
48 echo " OR" 55
49 echo " - Build a non-sdk image but include the profiling tools:" 56 if not self.remote:
50 echo " [ edit local.conf and add 'tools-profile' to the end of" 57 if not self.module:
51 echo " the EXTRA_IMAGE_FEATURES variable ]" 58 # derive module name from script
52 echo " $ bitbake core-image-sato" 59 self.module = os.path.basename(self.script)
53 echo "" 60 if self.module[-4:] == ".stp":
54 echo " [ NOTE that 'crosstap' needs to be able to ssh into the target" 61 self.module = self.module[:-4]
55 echo " system, which isn't enabled by default in -minimal images. ]" 62 # replace - if any with _
56 echo "" 63 self.module = self.module.replace("-", "_")
57 echo "Once you've build the image on the host system, you're ready to" 64
58 echo "boot it (or the equivalent pre-built image) and use 'crosstap'" 65 def command(self, args):
59 echo "to probe it (you need to source the environment as usual first):" 66 ret = []
60 echo "" 67 ret.append(self.stap)
61 echo " $ source oe-init-build-env" 68
62 echo " $ cd ~/my/systemtap/scripts" 69 if self.remote:
63 echo " $ crosstap root@192.168.1.xxx myscript.stp" 70 ret.append("--remote")
64 echo "" 71 ret.append(self.remote)
65} 72 else:
66 73 ret.append("-p4")
67function systemtap_target_arch() { 74 ret.append("-m")
68 SYSTEMTAP_TARGET_ARCH=$1 75 ret.append(self.module)
69 case $SYSTEMTAP_TARGET_ARCH in 76
70 i?86) 77 ret.append("-a")
71 SYSTEMTAP_TARGET_ARCH="i386" 78 ret.append(self.arch)
72 ;; 79
73 x86?64*) 80 ret.append("-B")
74 SYSTEMTAP_TARGET_ARCH="x86_64" 81 ret.append("CROSS_COMPILE=" + self.cross_compile)
75 ;; 82
76 arm*) 83 ret.append("-r")
77 SYSTEMTAP_TARGET_ARCH="arm" 84 ret.append(self.kernel_release)
78 ;; 85
79 powerpc*) 86 ret.append("-I")
80 SYSTEMTAP_TARGET_ARCH="powerpc" 87 ret.append(self.tapset)
81 ;; 88
82 *) 89 ret.append("-R")
83 ;; 90 ret.append(self.runtime)
84 esac 91
85} 92 if self.sysroot:
86 93 ret.append("--sysroot")
87if [ $# -lt 2 ]; then 94 ret.append(self.sysroot)
88 usage 95
89 exit 1 96 ret.append("--sysenv=PATH=" + self.target_path)
90fi 97 ret.append("--sysenv=LD_LIBRARY_PATH=" + self.target_ld_library_path)
91 98
92if [ -z "$BUILDDIR" ]; then 99 ret = ret + args
93 echo "Error: Unable to find the BUILDDIR environment variable." 100
94 echo "Did you forget to source your build system environment setup script?" 101 ret.append(self.script)
95 exit 1 102 return ret
96fi 103
97 104 def additional_environment(self):
98pushd $PWD 105 ret = {}
99cd $BUILDDIR 106 ret["SYSTEMTAP_DEBUGINFO_PATH"] = "+:.debug:build"
100BITBAKE_VARS=`bitbake -e virtual/kernel` 107 return ret
101popd 108
102 109 def environment(self):
103STAGING_BINDIR_TOOLCHAIN=$(echo "$BITBAKE_VARS" | grep ^STAGING_BINDIR_TOOLCHAIN \ 110 ret = os.environ.copy()
104 | cut -d '=' -f2 | cut -d '"' -f2) 111 additional = self.additional_environment()
105STAGING_BINDIR_TOOLPREFIX=$(echo "$BITBAKE_VARS" | grep ^TARGET_PREFIX \ 112 for e in additional:
106 | cut -d '=' -f2 | cut -d '"' -f2) 113 ret[e] = additional[e]
107TARGET_ARCH=$(echo "$BITBAKE_VARS" | grep ^TRANSLATED_TARGET_ARCH \ 114 return ret
108 | cut -d '=' -f2 | cut -d '"' -f2) 115
109TARGET_KERNEL_BUILDDIR=$(echo "$BITBAKE_VARS" | grep ^B= \ 116 def display_command(self, args):
110 | cut -d '=' -f2 | cut -d '"' -f2) 117 additional_env = self.additional_environment()
111 118 command = self.command(args)
112# Build and populate the recipe-sysroot-native with systemtap-native 119
113pushd $PWD 120 print("#!/bin/sh")
114cd $BUILDDIR 121 for e in additional_env:
115BITBAKE_VARS=`bitbake -e systemtap-native` 122 print("export %s=\"%s\"" % (e, additional_env[e]))
116popd 123 print(" ".join(command))
117SYSTEMTAP_HOST_INSTALLDIR=$(echo "$BITBAKE_VARS" | grep ^STAGING_DIR_NATIVE \ 124
118 | cut -d '=' -f2 | cut -d '"' -f2) 125class BitbakeEnvInvocationException(Exception):
119 126 def __init__(self, message):
120systemtap_target_arch "$TARGET_ARCH" 127 self.message = message
121 128
122if [ ! -d $TARGET_KERNEL_BUILDDIR ] || 129class BitbakeEnv(object):
123 [ ! -f $TARGET_KERNEL_BUILDDIR/vmlinux ]; then 130 BITBAKE="bitbake"
124 echo -e "\nError: No target kernel build found." 131
125 echo -e "Did you forget to create a local build of your image?" 132 def __init__(self, package):
126 setup_usage 133 self.package = package
127 exit 1 134 self.cmd = BitbakeEnv.BITBAKE + " -e " + self.package
128fi 135 self.popen = subprocess.Popen(self.cmd, shell=True,
129 136 stdout=subprocess.PIPE,
130if [ ! -f $SYSTEMTAP_HOST_INSTALLDIR/usr/bin/stap ]; then 137 stderr=subprocess.STDOUT)
131 echo -e "\nError: Native (host) systemtap not found." 138 self.__lines = self.popen.stdout.readlines()
132 echo -e "Did you accidentally build a local non-sdk image? (or forget to" 139 self.popen.wait()
133 echo -e "add 'tools-profile' to EXTRA_IMAGE_FEATURES in your local.conf)?" 140
134 echo -e "You can also: bitbake -c addto_recipe_sysroot systemtap-native" 141 self.lines = []
135 setup_usage 142 for line in self.__lines:
136 exit 1 143 self.lines.append(line.decode('utf-8'))
137fi 144
138 145 def get_vars(self, vars):
139target_user_hostname="$1" 146 if self.popen.returncode:
140full_script_name="$2" 147 raise BitbakeEnvInvocationException(
141script_name=$(basename "$2") 148 "\nFailed to execute '" + self.cmd +
142script_base=${script_name%.*} 149 "' with the following message:\n" +
143shift 2 150 ''.join(self.lines))
144 151
145${SYSTEMTAP_HOST_INSTALLDIR}/usr/bin/stap \ 152 search_patterns = []
146 -a ${SYSTEMTAP_TARGET_ARCH} \ 153 retdict = {}
147 -B CROSS_COMPILE="${STAGING_BINDIR_TOOLCHAIN}/${STAGING_BINDIR_TOOLPREFIX}" \ 154 for var in vars:
148 -r ${TARGET_KERNEL_BUILDDIR} \ 155 # regular not exported variable
149 -I ${SYSTEMTAP_HOST_INSTALLDIR}/usr/share/systemtap/tapset \ 156 rexpr = "^" + var + "=\"(.*)\""
150 -R ${SYSTEMTAP_HOST_INSTALLDIR}/usr/share/systemtap/runtime \ 157 re_compiled = re.compile(rexpr)
151 --remote=$target_user_hostname \ 158 search_patterns.append((var, re_compiled))
152 -m $script_base \ 159
153 $full_script_name "$@" 160 # exported variable
154 161 rexpr = "^export " + var + "=\"(.*)\""
155exit 0 162 re_compiled = re.compile(rexpr)
163 search_patterns.append((var, re_compiled))
164
165 for line in self.lines:
166 for var, rexpr in search_patterns:
167 m = rexpr.match(line)
168 if m:
169 value = m.group(1)
170 retdict[var] = value
171
172 # fill variables values in order how they were requested
173 ret = []
174 for var in vars:
175 ret.append(retdict.get(var))
176
177 # if it is single value list return it as scalar, not the list
178 if len(ret) == 1:
179 ret = ret[0]
180
181 return ret
182
183class ParamDiscovery(object):
184 SYMBOLS_CHECK_MESSAGE = """
185WARNING: image '%s' does not have dbg-pkgs IMAGE_FEATURES enabled and no
186"image-combined-dbg" in inherited classes is specified. As result the image
187does not have symbols for user-land processes DWARF based probes. Consider
188adding 'dbg-pkgs' to EXTRA_IMAGE_FEATURES or adding "image-combined-dbg" to
189USER_CLASSES. I.e add this line 'USER_CLASSES += "image-combined-dbg"' to
190local.conf file.
191
192Or you may use IMAGE_GEN_DEBUGFS="1" option, and then after build you need
193recombine/unpack image and image-dbg tarballs and pass resulting dir location
194with --sysroot option.
195"""
196
197 def __init__(self, image):
198 self.image = image
199
200 self.image_rootfs = None
201 self.image_features = None
202 self.image_gen_debugfs = None
203 self.inherit = None
204 self.base_bindir = None
205 self.base_sbindir = None
206 self.base_libdir = None
207 self.bindir = None
208 self.sbindir = None
209 self.libdir = None
210
211 self.staging_bindir_toolchain = None
212 self.target_prefix = None
213 self.target_arch = None
214 self.target_kernel_builddir = None
215
216 self.staging_dir_native = None
217
218 self.image_combined_dbg = False
219
220 def discover(self):
221 if self.image:
222 benv_image = BitbakeEnv(self.image)
223 (self.image_rootfs,
224 self.image_features,
225 self.image_gen_debugfs,
226 self.inherit,
227 self.base_bindir,
228 self.base_sbindir,
229 self.base_libdir,
230 self.bindir,
231 self.sbindir,
232 self.libdir
233 ) = benv_image.get_vars(
234 ("IMAGE_ROOTFS",
235 "IMAGE_FEATURES",
236 "IMAGE_GEN_DEBUGFS",
237 "INHERIT",
238 "base_bindir",
239 "base_sbindir",
240 "base_libdir",
241 "bindir",
242 "sbindir",
243 "libdir"
244 ))
245
246 benv_kernel = BitbakeEnv("virtual/kernel")
247 (self.staging_bindir_toolchain,
248 self.target_prefix,
249 self.target_arch,
250 self.target_kernel_builddir
251 ) = benv_kernel.get_vars(
252 ("STAGING_BINDIR_TOOLCHAIN",
253 "TARGET_PREFIX",
254 "TRANSLATED_TARGET_ARCH",
255 "B"
256 ))
257
258 benv_systemtap = BitbakeEnv("systemtap-native")
259 (self.staging_dir_native
260 ) = benv_systemtap.get_vars(["STAGING_DIR_NATIVE"])
261
262 if self.inherit:
263 if "image-combined-dbg" in self.inherit.split():
264 self.image_combined_dbg = True
265
266 def check(self, sysroot_option):
267 ret = True
268 if self.image_rootfs:
269 sysroot = self.image_rootfs
270 if not os.path.isdir(self.image_rootfs):
271 print("ERROR: Cannot find '" + sysroot +
272 "' directory. Was '" + self.image + "' image built?")
273 ret = False
274
275 stap = self.staging_dir_native + "/usr/bin/stap"
276 if not os.path.isfile(stap):
277 print("ERROR: Cannot find '" + stap +
278 "'. Was 'systemtap-native' built?")
279 ret = False
280
281 if not os.path.isdir(self.target_kernel_builddir):
282 print("ERROR: Cannot find '" + self.target_kernel_builddir +
283 "' directory. Was 'kernel/virtual' built?")
284 ret = False
285
286 if not sysroot_option and self.image_rootfs:
287 dbg_pkgs_found = False
288
289 if self.image_features:
290 image_features = self.image_features.split()
291 if "dbg-pkgs" in image_features:
292 dbg_pkgs_found = True
293
294 if not dbg_pkgs_found \
295 and not self.image_combined_dbg:
296 print(ParamDiscovery.SYMBOLS_CHECK_MESSAGE % (self.image))
297
298 if not ret:
299 print("")
300
301 return ret
302
303 def __map_systemtap_arch(self):
304 a = self.target_arch
305 ret = a
306 if re.match('(athlon|x86.64)$', a):
307 ret = 'x86_64'
308 elif re.match('i.86$', a):
309 ret = 'i386'
310 elif re.match('arm$', a):
311 ret = 'arm'
312 elif re.match('aarch64$', a):
313 ret = 'arm64'
314 elif re.match('mips(isa|)(32|64|)(r6|)(el|)$', a):
315 ret = 'mips'
316 elif re.match('p(pc|owerpc)(|64)', a):
317 ret = 'powerpc'
318 return ret
319
320 def fill_stap(self, stap):
321 stap.stap = self.staging_dir_native + "/usr/bin/stap"
322 if not stap.sysroot:
323 if self.image_rootfs:
324 if self.image_combined_dbg:
325 stap.sysroot = self.image_rootfs + "-dbg"
326 else:
327 stap.sysroot = self.image_rootfs
328 stap.runtime = self.staging_dir_native + "/usr/share/systemtap/runtime"
329 stap.tapset = self.staging_dir_native + "/usr/share/systemtap/tapset"
330 stap.arch = self.__map_systemtap_arch()
331 stap.cross_compile = self.staging_bindir_toolchain + "/" + \
332 self.target_prefix
333 stap.kernel_release = self.target_kernel_builddir
334
335 # do we have standard that tells in which order these need to appear
336 target_path = []
337 if self.sbindir:
338 target_path.append(self.sbindir)
339 if self.bindir:
340 target_path.append(self.bindir)
341 if self.base_sbindir:
342 target_path.append(self.base_sbindir)
343 if self.base_bindir:
344 target_path.append(self.base_bindir)
345 stap.target_path = ":".join(target_path)
346
347 target_ld_library_path = []
348 if self.libdir:
349 target_ld_library_path.append(self.libdir)
350 if self.base_libdir:
351 target_ld_library_path.append(self.base_libdir)
352 stap.target_ld_library_path = ":".join(target_ld_library_path)
353
354
355def main():
356 usage = """usage: %prog -s <systemtap-script> [options] [-- [systemtap options]]
357
358%prog cross compile given SystemTap script against given image, kernel
359
360It needs to run in environtment set for bitbake - it uses bitbake -e
361invocations to retrieve information to construct proper stap cross build
362invocation arguments. It assumes that systemtap-native is built in given
363bitbake workspace.
364
365Anything after -- option is passed directly to stap.
366
367Legacy script invocation style supported but depreciated:
368 %prog <user@hostname> <sytemtap-script> [systemtap options]
369
370To enable most out of systemtap the following site.conf or local.conf
371configuration is recommended:
372
373# enables symbol + target binaries rootfs-dbg in workspace
374IMAGE_GEN_DEBUGFS = "1"
375IMAGE_FSTYPES_DEBUGFS = "tar.bz2"
376USER_CLASSES += "image-combined-dbg"
377
378# enables kernel debug symbols
379KERNEL_EXTRA_FEATURES_append = " features/debug/debug-kernel.scc"
380
381# minimal, just run-time systemtap configuration in target image
382PACKAGECONFIG_pn-systemtap = "monitor"
383
384# add systemtap run-time into target image if it is not there yet
385IMAGE_INSTALL_append = " systemtap"
386"""
387 option_parser = optparse.OptionParser(usage=usage)
388
389 option_parser.add_option("-s", "--script", dest="script",
390 help="specify input script FILE name",
391 metavar="FILE")
392
393 option_parser.add_option("-i", "--image", dest="image",
394 help="specify image name for which script should be compiled")
395
396 option_parser.add_option("-r", "--remote", dest="remote",
397 help="specify username@hostname of remote target to run script "
398 "optional, it assumes that remote target can be accessed through ssh")
399
400 option_parser.add_option("-m", "--module", dest="module",
401 help="specify module name, optional, has effect only if --remote is not used, "
402 "if not specified module name will be derived from passed script name")
403
404 option_parser.add_option("-y", "--sysroot", dest="sysroot",
405 help="explicitely specify image sysroot location. May need to use it in case "
406 "when IMAGE_GEN_DEBUGFS=\"1\" option is used and recombined with symbols "
407 "in different location",
408 metavar="DIR")
409
410 option_parser.add_option("-o", "--out", dest="out",
411 action="store_true",
412 help="output shell script that equvivalent invocation of this script with "
413 "given set of arguments, in given bitbake environment. It could be stored in "
414 "separate shell script and could be repeated without incuring bitbake -e "
415 "invocation overhead",
416 default=False)
417
418 option_parser.add_option("-d", "--debug", dest="debug",
419 action="store_true",
420 help="enable debug output. Use this option to see resulting stap invocation",
421 default=False)
422
423 # is invocation follow syntax from orignal crosstap shell script
424 legacy_args = False
425
426 # check if we called the legacy way
427 if len(sys.argv) >= 3:
428 if sys.argv[1].find("@") != -1 and os.path.exists(sys.argv[2]):
429 legacy_args = True
430
431 # fill options values for legacy invocation case
432 options = optparse.Values
433 options.script = sys.argv[2]
434 options.remote = sys.argv[1]
435 options.image = None
436 options.module = None
437 options.sysroot = None
438 options.out = None
439 options.debug = None
440 remaining_args = sys.argv[3:]
441
442 if not legacy_args:
443 (options, remaining_args) = option_parser.parse_args()
444
445 if not options.script or not os.path.exists(options.script):
446 print("'-s FILE' option is missing\n")
447 option_parser.print_help()
448 else:
449 stap = Stap(options.script, options.module, options.remote)
450 discovery = ParamDiscovery(options.image)
451 discovery.discover()
452 if not discovery.check(options.sysroot):
453 option_parser.print_help()
454 else:
455 stap.sysroot = options.sysroot
456 discovery.fill_stap(stap)
457
458 if options.out:
459 stap.display_command(remaining_args)
460 else:
461 cmd = stap.command(remaining_args)
462 env = stap.environment()
463
464 if options.debug:
465 print(" ".join(cmd))
466
467 os.execve(cmd[0], cmd, env)
468
469main()