| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 1 | #!/usr/bin/env python3 | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 2 | # | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 3 | # Build a systemtap script for a given image, kernel | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 4 | # | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 5 | # Effectively script extracts needed information from set of | 
 | 6 | # 'bitbake -e' commands and contructs proper invocation of stap on | 
 | 7 | # host to build systemtap script for a given target. | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 8 | # | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 9 | # By default script will compile scriptname.ko that could be copied | 
 | 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. | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 13 | # | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 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. | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 20 | # All rights reserved. | 
 | 21 | # | 
 | 22 | # This program is free software; you can redistribute it and/or modify | 
 | 23 | # it under the terms of the GNU General Public License version 2 as | 
 | 24 | # published by the Free Software Foundation. | 
 | 25 | # | 
 | 26 | # This program is distributed in the hope that it will be useful, | 
 | 27 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 28 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | 
 | 29 | # See the GNU General Public License for more details. | 
 | 30 | # | 
 | 31 | # You should have received a copy of the GNU General Public License | 
 | 32 | # along with this program; if not, write to the Free Software | 
 | 33 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 
 | 34 |  | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 35 | import sys | 
 | 36 | import re | 
 | 37 | import subprocess | 
 | 38 | import os | 
 | 39 | import optparse | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 40 |  | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 41 | class Stap(object): | 
 | 42 |     def __init__(self, script, module, remote): | 
 | 43 |         self.script = script | 
 | 44 |         self.module = module | 
 | 45 |         self.remote = remote | 
 | 46 |         self.stap = None | 
 | 47 |         self.sysroot = None | 
 | 48 |         self.runtime = None | 
 | 49 |         self.tapset = None | 
 | 50 |         self.arch = None | 
 | 51 |         self.cross_compile = None | 
 | 52 |         self.kernel_release = None | 
 | 53 |         self.target_path = None | 
 | 54 |         self.target_ld_library_path = None | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 55 |  | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 56 |         if not self.remote: | 
 | 57 |             if not self.module: | 
 | 58 |                 # derive module name from script | 
 | 59 |                 self.module = os.path.basename(self.script) | 
 | 60 |                 if self.module[-4:] == ".stp": | 
 | 61 |                     self.module = self.module[:-4] | 
 | 62 |                     # replace - if any with _ | 
 | 63 |                     self.module = self.module.replace("-", "_") | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 64 |  | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 65 |     def command(self, args): | 
 | 66 |         ret = [] | 
 | 67 |         ret.append(self.stap) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 68 |  | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 69 |         if self.remote: | 
 | 70 |             ret.append("--remote") | 
 | 71 |             ret.append(self.remote) | 
 | 72 |         else: | 
 | 73 |             ret.append("-p4") | 
 | 74 |             ret.append("-m") | 
 | 75 |             ret.append(self.module) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 76 |  | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 77 |         ret.append("-a") | 
 | 78 |         ret.append(self.arch) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 79 |  | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 80 |         ret.append("-B") | 
 | 81 |         ret.append("CROSS_COMPILE=" + self.cross_compile) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 82 |  | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 83 |         ret.append("-r") | 
 | 84 |         ret.append(self.kernel_release) | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 85 |  | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 86 |         ret.append("-I") | 
 | 87 |         ret.append(self.tapset) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 88 |  | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 89 |         ret.append("-R") | 
 | 90 |         ret.append(self.runtime) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 91 |  | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 92 |         if self.sysroot: | 
 | 93 |             ret.append("--sysroot") | 
 | 94 |             ret.append(self.sysroot) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 95 |  | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 96 |             ret.append("--sysenv=PATH=" + self.target_path) | 
 | 97 |             ret.append("--sysenv=LD_LIBRARY_PATH=" + self.target_ld_library_path) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 98 |  | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 99 |         ret = ret + args | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 100 |  | 
| Brad Bishop | 316dfdd | 2018-06-25 12:45:53 -0400 | [diff] [blame] | 101 |         ret.append(self.script) | 
 | 102 |         return ret | 
 | 103 |  | 
 | 104 |     def additional_environment(self): | 
 | 105 |         ret = {} | 
 | 106 |         ret["SYSTEMTAP_DEBUGINFO_PATH"] = "+:.debug:build" | 
 | 107 |         return ret | 
 | 108 |  | 
 | 109 |     def environment(self): | 
 | 110 |         ret = os.environ.copy() | 
 | 111 |         additional = self.additional_environment() | 
 | 112 |         for e in additional: | 
 | 113 |             ret[e] = additional[e] | 
 | 114 |         return ret | 
 | 115 |  | 
 | 116 |     def display_command(self, args): | 
 | 117 |         additional_env = self.additional_environment() | 
 | 118 |         command = self.command(args) | 
 | 119 |  | 
 | 120 |         print("#!/bin/sh") | 
 | 121 |         for e in additional_env: | 
 | 122 |             print("export %s=\"%s\"" % (e, additional_env[e])) | 
 | 123 |         print(" ".join(command)) | 
 | 124 |  | 
 | 125 | class BitbakeEnvInvocationException(Exception): | 
 | 126 |     def __init__(self, message): | 
 | 127 |         self.message = message | 
 | 128 |  | 
 | 129 | class BitbakeEnv(object): | 
 | 130 |     BITBAKE="bitbake" | 
 | 131 |  | 
 | 132 |     def __init__(self, package): | 
 | 133 |         self.package = package | 
 | 134 |         self.cmd = BitbakeEnv.BITBAKE + " -e " + self.package | 
 | 135 |         self.popen = subprocess.Popen(self.cmd, shell=True, | 
 | 136 |                                       stdout=subprocess.PIPE, | 
 | 137 |                                       stderr=subprocess.STDOUT) | 
 | 138 |         self.__lines = self.popen.stdout.readlines() | 
 | 139 |         self.popen.wait() | 
 | 140 |  | 
 | 141 |         self.lines = [] | 
 | 142 |         for line in self.__lines: | 
 | 143 |                 self.lines.append(line.decode('utf-8')) | 
 | 144 |  | 
 | 145 |     def get_vars(self, vars): | 
 | 146 |         if self.popen.returncode: | 
 | 147 |             raise BitbakeEnvInvocationException( | 
 | 148 |                 "\nFailed to execute '" + self.cmd + | 
 | 149 |                 "' with the following message:\n" + | 
 | 150 |                 ''.join(self.lines)) | 
 | 151 |  | 
 | 152 |         search_patterns = [] | 
 | 153 |         retdict = {} | 
 | 154 |         for var in vars: | 
 | 155 |             # regular not exported variable | 
 | 156 |             rexpr = "^" + var + "=\"(.*)\"" | 
 | 157 |             re_compiled = re.compile(rexpr) | 
 | 158 |             search_patterns.append((var, re_compiled)) | 
 | 159 |  | 
 | 160 |             # exported variable | 
 | 161 |             rexpr = "^export " + var + "=\"(.*)\"" | 
 | 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 |  | 
 | 183 | class ParamDiscovery(object): | 
 | 184 |     SYMBOLS_CHECK_MESSAGE = """ | 
 | 185 | WARNING: 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 | 
 | 187 | does not have symbols for user-land processes DWARF based probes. Consider | 
 | 188 | adding 'dbg-pkgs' to EXTRA_IMAGE_FEATURES or adding "image-combined-dbg" to | 
 | 189 | USER_CLASSES. I.e add this line 'USER_CLASSES += "image-combined-dbg"' to | 
 | 190 | local.conf file. | 
 | 191 |  | 
 | 192 | Or you may use IMAGE_GEN_DEBUGFS="1" option, and then after build you need | 
 | 193 | recombine/unpack image and image-dbg tarballs and pass resulting dir location | 
 | 194 | with --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 |  | 
 | 355 | def main(): | 
 | 356 |     usage = """usage: %prog -s <systemtap-script> [options] [-- [systemtap options]] | 
 | 357 |  | 
 | 358 | %prog cross compile given SystemTap script against given image, kernel | 
 | 359 |  | 
 | 360 | It needs to run in environtment set for bitbake - it uses bitbake -e | 
 | 361 | invocations to retrieve information to construct proper stap cross build | 
 | 362 | invocation arguments. It assumes that systemtap-native is built in given | 
 | 363 | bitbake workspace. | 
 | 364 |  | 
 | 365 | Anything after -- option is passed directly to stap. | 
 | 366 |  | 
 | 367 | Legacy script invocation style supported but depreciated: | 
 | 368 |   %prog <user@hostname> <sytemtap-script> [systemtap options] | 
 | 369 |  | 
 | 370 | To enable most out of systemtap the following site.conf or local.conf | 
 | 371 | configuration is recommended: | 
 | 372 |  | 
 | 373 | # enables symbol + target binaries rootfs-dbg in workspace | 
 | 374 | IMAGE_GEN_DEBUGFS = "1" | 
 | 375 | IMAGE_FSTYPES_DEBUGFS = "tar.bz2" | 
 | 376 | USER_CLASSES += "image-combined-dbg" | 
 | 377 |  | 
 | 378 | # enables kernel debug symbols | 
 | 379 | KERNEL_EXTRA_FEATURES_append = " features/debug/debug-kernel.scc" | 
 | 380 |  | 
 | 381 | # minimal, just run-time systemtap configuration in target image | 
 | 382 | PACKAGECONFIG_pn-systemtap = "monitor" | 
 | 383 |  | 
 | 384 | # add systemtap run-time into target image if it is not there yet | 
 | 385 | IMAGE_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 |  | 
 | 469 | main() |