[v3,5/5] crosstap: replace script with new python based implementation

Message ID 1522952730-5967-6-git-send-email-kamensky@cisco.com
State Accepted
Commit 1cbbcf26e0a9ca6e0b34a89512bf75dbae8bfaf0
Headers show
Series
  • systemtap adding sysroot, cross compiling of user land related scripts
Related show

Commit Message

Victor Kamensky April 5, 2018, 6:25 p.m.
New crosstap python implementation is total replacement for
crosstap shell script, that has superseding capabilities.
New script support cross compiling of SystemTap scripts
for user-land, by using supplied image rootfs. Whereas old
script could only deal with scripts against kernel. New script
has more complex logic and additional capabilities.

As invocation interface new script support old "legacy"
mode and provides alternative new regular options interface
to access additional functionality.

Signed-off-by: Victor Kamensky <kamensky@cisco.com>

---
 scripts/crosstap | 586 ++++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 450 insertions(+), 136 deletions(-)

-- 
2.7.4

-- 
_______________________________________________
Openembedded-core mailing list
Openembedded-core@lists.openembedded.org
http://lists.openembedded.org/mailman/listinfo/openembedded-core

Comments

Ross Burton May 14, 2018, 8:10 a.m. | #1
On 5 April 2018 at 19:25, Victor Kamensky <kamensky@cisco.com> wrote:
> New crosstap python implementation is total replacement for

> crosstap shell script, that has superseding capabilities.

> New script support cross compiling of SystemTap scripts

> for user-land, by using supplied image rootfs. Whereas old

> script could only deal with scripts against kernel. New script

> has more complex logic and additional capabilities.


...

> +        benv_kernel = BitbakeEnv("virtual/kernel")

> +        (self.staging_bindir_toolchain,

> +         self.target_prefix,

> +         self.target_arch,

> +         self.target_kernel_builddir

> +        ) = benv_kernel.get_vars(

> +             ("STAGING_BINDIR_TOOLCHAIN",

> +              "TARGET_PREFIX",

> +              "TRANSLATED_TARGET_ARCH",

> +              "B"

> +            ))


> +        if not os.path.isdir(self.target_kernel_builddir):

> +            print("ERROR: Cannot find '" + self.target_kernel_builddir +

> +                  "' directory. Was 'kernel/virtual' built?")

> +            ret = False


You can't assume that B exists and is useful, as if virtual/kernel was
installed from sstate then all the installed pieces will be present
but $B will be empty.

For example, I re-ran oe-selftest on the autobuilder, the kernel was
extracted from sstate as the previous run built it, and the test
failed:

2018-05-13 22:36:08,905 - oe-selftest - INFO - FAIL:
test_crosstap_can_use_systemtap_on_qemu (crosstap.CrossTapTest)
2018-05-13 22:36:08,906 - oe-selftest - INFO -
----------------------------------------------------------------------
2018-05-13 22:36:08,906 - oe-selftest - INFO - Traceback (most recent
call last):
  File "/home/pokybuild/yocto-autobuilder/yocto-worker/nightly-oe-selftest/build/meta/lib/oeqa/selftest/cases/crosstap.py",
line 25, in test_crosstap_can_use_systemtap_on_qemu
    self.assertIn('hello world', result.output, 'Crosstap failed.')
AssertionError: 'hello world' not found in 'ERROR: Cannot find
\'/home/pokybuild/yocto-autobuilder/yocto-worker/nightly-oe-selftest/build/build/tmp/work/qemux86_64-poky-linux/linux-yocto/4.14.30+gitAUTOINC+ea9330894e_74f6cd2b69-r0/linux-qemux86_64-standard-build\'
directory. Was \'kernel/virtual\' built?

Ross
-- 
_______________________________________________
Openembedded-core mailing list
Openembedded-core@lists.openembedded.org
http://lists.openembedded.org/mailman/listinfo/openembedded-core
Victor Kamensky May 14, 2018, 6:25 p.m. | #2
On Mon, 14 May 2018, Burton, Ross wrote:

> On 5 April 2018 at 19:25, Victor Kamensky <kamensky@cisco.com> wrote:

>> New crosstap python implementation is total replacement for

>> crosstap shell script, that has superseding capabilities.

>> New script support cross compiling of SystemTap scripts

>> for user-land, by using supplied image rootfs. Whereas old

>> script could only deal with scripts against kernel. New script

>> has more complex logic and additional capabilities.

>

> ...

>

>> +        benv_kernel = BitbakeEnv("virtual/kernel")

>> +        (self.staging_bindir_toolchain,

>> +         self.target_prefix,

>> +         self.target_arch,

>> +         self.target_kernel_builddir

>> +        ) = benv_kernel.get_vars(

>> +             ("STAGING_BINDIR_TOOLCHAIN",

>> +              "TARGET_PREFIX",

>> +              "TRANSLATED_TARGET_ARCH",

>> +              "B"

>> +            ))

>

>> +        if not os.path.isdir(self.target_kernel_builddir):

>> +            print("ERROR: Cannot find '" + self.target_kernel_builddir +

>> +                  "' directory. Was 'kernel/virtual' built?")

>> +            ret = False

>

> You can't assume that B exists and is useful, as if virtual/kernel was

> installed from sstate then all the installed pieces will be present

> but $B will be empty.


Ross, thank you for pointing to this. I will look at it.

Please note use of B of kernel/virtual by crosstap preceeded my
commit:

c6da6b64 (Tom Zanussi 2012-08-31 00:06:57 -0500 109) 
TARGET_KERNEL_BUILDDIR=$(echo "$BITBAKE_VARS" | grep ^B= \
c6da6b64 (Tom Zanussi 2012-08-31 00:06:57 -0500 110)   | cut -d '=' -f2 | 
cut -d '"' -f2)

I just followed the same logic.

> For example, I re-ran oe-selftest on the autobuilder, the kernel was

> extracted from sstate as the previous run built it, and the test

> failed:


Systemtap translator generates kernel module source and it needs
cross compiled environment to build it against proper kernel sources
build artifacts. I.e cross compilation of kernel module should
be supported when kernel is extracted from sstate.

So my thought was to figure out how to cross compile kernel module
against kernel/virtual sstate, I can take a look how recipe that
'inherit module' works in such case. So I've tried the following in
tree pulled today (cryptodev-module is recipe example that
does 'inherit module'):

Variant1:

bitbake cryptodev-module
bitbake -c clean linux-yocto
bitbake -c cleansstate cryptodev-module
bitbake cryptodev-module

Variant2:

bitbake cryptodev-module
mv tmp-glibc tmp-glibc.bak
bitbake linux-yocto
bitbake cryptodev-module

In both cases I see that full kernel rebuild (pull source, compile,
etc) is retriggered by building cryptodev-module, even if
linux-yocto initially is pulled from sstate. In Variant1
cryptodev-module build actually failed because some host utilities
from kernel tree (like scripts/basic/fixdep) were not rebuilt.

I don't think it is proper for crosstap utility to retrigger
full kernel/virtual build. So unless kernel module build against
kernel/virtual sstate without full rebuild of kernel/virtual is
solved I am not sure what I can do in crosstap script.

Currently I see that work-shared/$MACHINE/kernel-source and
work-shared/$MACHINE/kernel-build-artifacts are created and it
seems their purpose is to facilitate external kernel modules
build, and maybe need to full kernel rebuild will be fixed soon.

Do I miss something? Any advise how to address this problem
is appreciated.

Thanks,
Victor

> 2018-05-13 22:36:08,905 - oe-selftest - INFO - FAIL:

> test_crosstap_can_use_systemtap_on_qemu (crosstap.CrossTapTest)

> 2018-05-13 22:36:08,906 - oe-selftest - INFO -

> ----------------------------------------------------------------------

> 2018-05-13 22:36:08,906 - oe-selftest - INFO - Traceback (most recent

> call last):

>  File "/home/pokybuild/yocto-autobuilder/yocto-worker/nightly-oe-selftest/build/meta/lib/oeqa/selftest/cases/crosstap.py",

> line 25, in test_crosstap_can_use_systemtap_on_qemu

>    self.assertIn('hello world', result.output, 'Crosstap failed.')

> AssertionError: 'hello world' not found in 'ERROR: Cannot find

> \'/home/pokybuild/yocto-autobuilder/yocto-worker/nightly-oe-selftest/build/build/tmp/work/qemux86_64-poky-linux/linux-yocto/4.14.30+gitAUTOINC+ea9330894e_74f6cd2b69-r0/linux-qemux86_64-standard-build\'

> directory. Was \'kernel/virtual\' built?

>

> Ross

>

-- 
_______________________________________________
Openembedded-core mailing list
Openembedded-core@lists.openembedded.org
http://lists.openembedded.org/mailman/listinfo/openembedded-core
Bruce Ashfield May 15, 2018, 1:20 p.m. | #3
On 05/14/2018 02:25 PM, Victor Kamensky wrote:
> 
> 
> On Mon, 14 May 2018, Burton, Ross wrote:
> 
>> On 5 April 2018 at 19:25, Victor Kamensky <kamensky@cisco.com> wrote:
>>> New crosstap python implementation is total replacement for
>>> crosstap shell script, that has superseding capabilities.
>>> New script support cross compiling of SystemTap scripts
>>> for user-land, by using supplied image rootfs. Whereas old
>>> script could only deal with scripts against kernel. New script
>>> has more complex logic and additional capabilities.
>>
>> ...
>>
>>> +        benv_kernel = BitbakeEnv("virtual/kernel")
>>> +        (self.staging_bindir_toolchain,
>>> +         self.target_prefix,
>>> +         self.target_arch,
>>> +         self.target_kernel_builddir
>>> +        ) = benv_kernel.get_vars(
>>> +             ("STAGING_BINDIR_TOOLCHAIN",
>>> +              "TARGET_PREFIX",
>>> +              "TRANSLATED_TARGET_ARCH",
>>> +              "B"
>>> +            ))
>>
>>> +        if not os.path.isdir(self.target_kernel_builddir):
>>> +            print("ERROR: Cannot find '" + 
>>> self.target_kernel_builddir +
>>> +                  "' directory. Was 'kernel/virtual' built?")
>>> +            ret = False
>>
>> You can't assume that B exists and is useful, as if virtual/kernel was
>> installed from sstate then all the installed pieces will be present
>> but $B will be empty.
> 
> Ross, thank you for pointing to this. I will look at it.
> 
> Please note use of B of kernel/virtual by crosstap preceeded my
> commit:
> 
> c6da6b64 (Tom Zanussi 2012-08-31 00:06:57 -0500 109) 
> TARGET_KERNEL_BUILDDIR=$(echo "$BITBAKE_VARS" | grep ^B= \
> c6da6b64 (Tom Zanussi 2012-08-31 00:06:57 -0500 110)   | cut -d '=' -f2 
> | cut -d '"' -f2)
> 
> I just followed the same logic.
> 
>> For example, I re-ran oe-selftest on the autobuilder, the kernel was
>> extracted from sstate as the previous run built it, and the test
>> failed:
> 
> Systemtap translator generates kernel module source and it needs
> cross compiled environment to build it against proper kernel sources
> build artifacts. I.e cross compilation of kernel module should
> be supported when kernel is extracted from sstate.
> 
> So my thought was to figure out how to cross compile kernel module
> against kernel/virtual sstate, I can take a look how recipe that
> 'inherit module' works in such case. So I've tried the following in
> tree pulled today (cryptodev-module is recipe example that
> does 'inherit module'):
> 
> Variant1:
> 
> bitbake cryptodev-module
> bitbake -c clean linux-yocto
> bitbake -c cleansstate cryptodev-module
> bitbake cryptodev-module
> 
> Variant2:
> 
> bitbake cryptodev-module
> mv tmp-glibc tmp-glibc.bak
> bitbake linux-yocto
> bitbake cryptodev-module
> 
> In both cases I see that full kernel rebuild (pull source, compile,
> etc) is retriggered by building cryptodev-module, even if
> linux-yocto initially is pulled from sstate. In Variant1
> cryptodev-module build actually failed because some host utilities
> from kernel tree (like scripts/basic/fixdep) were not rebuilt.
> 
> I don't think it is proper for crosstap utility to retrigger
> full kernel/virtual build. So unless kernel module build against
> kernel/virtual sstate without full rebuild of kernel/virtual is
> solved I am not sure what I can do in crosstap script.

I recall some work that we did on this 2 (or was it 3) year ago
now .. but can't dig up the thread references. The choices were
to have the helper utilities in sstate or trigger their rebuild
from the shared source + build artifacts. Obviously the outcome
of that work was not a complete solution.

> 
> Currently I see that work-shared/$MACHINE/kernel-source and
> work-shared/$MACHINE/kernel-build-artifacts are created and it
> seems their purpose is to facilitate external kernel modules
> build, and maybe need to full kernel rebuild will be fixed soon.

Correct. The build artifacts are enough to build external
modules, but not a full kernel rebuild, that is what the kernel-devsrc
does. But even those full kernel rebuilds do required the scripts/helper
utilities to be rebuilt.

The recipes that inherit modules really should be triggering the
rebuild of those helper scripts if they don't exist, so if that
isn't working, or not enough of the kernel build infrastructure is
in the build-artifacts directory .. that is an area to look into
more.

Bruce

> 
> Do I miss something? Any advise how to address this problem
> is appreciated.
> 
> Thanks,
> Victor
> 
>> 2018-05-13 22:36:08,905 - oe-selftest - INFO - FAIL:
>> test_crosstap_can_use_systemtap_on_qemu (crosstap.CrossTapTest)
>> 2018-05-13 22:36:08,906 - oe-selftest - INFO -
>> ----------------------------------------------------------------------
>> 2018-05-13 22:36:08,906 - oe-selftest - INFO - Traceback (most recent
>> call last):
>>  File 
>> "/home/pokybuild/yocto-autobuilder/yocto-worker/nightly-oe-selftest/build/meta/lib/oeqa/selftest/cases/crosstap.py", 
>>
>> line 25, in test_crosstap_can_use_systemtap_on_qemu
>>    self.assertIn('hello world', result.output, 'Crosstap failed.')
>> AssertionError: 'hello world' not found in 'ERROR: Cannot find
>> \'/home/pokybuild/yocto-autobuilder/yocto-worker/nightly-oe-selftest/build/build/tmp/work/qemux86_64-poky-linux/linux-yocto/4.14.30+gitAUTOINC+ea9330894e_74f6cd2b69-r0/linux-qemux86_64-standard-build\' 
>>
>> directory. Was \'kernel/virtual\' built?
>>
>> Ross
>>

Patch

diff --git a/scripts/crosstap b/scripts/crosstap
index 39739bb..e33fa4a 100755
--- a/scripts/crosstap
+++ b/scripts/crosstap
@@ -1,15 +1,22 @@ 
-#!/bin/bash
+#!/usr/bin/env python3
 #
-# Run a systemtap script on remote target
+# Build a systemtap script for a given image, kernel
 #
-# Examples (run on build host, target is 192.168.1.xxx):
-#   $ source oe-init-build-env"
-#   $ cd ~/my/systemtap/scripts"
+# Effectively script extracts needed information from set of
+# 'bitbake -e' commands and contructs proper invocation of stap on
+# host to build systemtap script for a given target.
 #
-#   $ crosstap root@192.168.1.xxx myscript.stp"
-#   $ crosstap root@192.168.1.xxx myscript-with-args.stp 99 ninetynine"
+# By default script will compile scriptname.ko that could be copied
+# to taget and activated with 'staprun scriptname.ko' command. Or if
+# --remote user@hostname option is specified script will build, load
+# execute script on target.
 #
-# Copyright (c) 2012, Intel Corporation.
+# This script is very similar and inspired by crosstap shell script.
+# The major difference that this script supports user-land related
+# systemtap script, whereas crosstap could deal only with scripts
+# related to kernel.
+#
+# Copyright (c) 2018, Cisco Systems.
 # All rights reserved.
 #
 # This program is free software; you can redistribute it and/or modify
@@ -25,131 +32,438 @@ 
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 
-function usage() {
-    echo "Usage: $0 <user@hostname> <sytemtap-script> [additional systemtap-script args]"
-}
-
-function setup_usage() {
-    echo ""
-    echo "'crosstap' requires a local sdk build of the target system"
-    echo "(or a build that includes 'tools-profile') in order to build"
-    echo "kernel modules that can probe the target system."
-    echo ""
-    echo "Practically speaking, that means you need to do the following:"
-    echo "  - If you're running a pre-built image, download the release"
-    echo "    and/or BSP tarballs used to build the image."
-    echo "  - If you're working from git sources, just clone the metadata"
-    echo "    and BSP layers needed to build the image you'll be booting."
-    echo "  - Make sure you're properly set up to build a new image (see"
-    echo "    the BSP README and/or the widely available basic documentation"
-    echo "    that discusses how to build images)."
-    echo "  - Build an -sdk version of the image e.g.:"
-    echo "      $ bitbake core-image-sato-sdk"
-    echo "  OR"
-    echo "  - Build a non-sdk image but include the profiling tools:"
-    echo "      [ edit local.conf and add 'tools-profile' to the end of"
-    echo "        the EXTRA_IMAGE_FEATURES variable ]"
-    echo "      $ bitbake core-image-sato"
-    echo ""
-    echo "  [ NOTE that 'crosstap' needs to be able to ssh into the target"
-    echo "    system, which isn't enabled by default in -minimal images. ]"
-    echo ""
-    echo "Once you've build the image on the host system, you're ready to"
-    echo "boot it (or the equivalent pre-built image) and use 'crosstap'"
-    echo "to probe it (you need to source the environment as usual first):"
-    echo ""
-    echo "    $ source oe-init-build-env"
-    echo "    $ cd ~/my/systemtap/scripts"
-    echo "    $ crosstap root@192.168.1.xxx myscript.stp"
-    echo ""
-}
-
-function systemtap_target_arch() {
-    SYSTEMTAP_TARGET_ARCH=$1
-    case $SYSTEMTAP_TARGET_ARCH in
-        i?86)
-            SYSTEMTAP_TARGET_ARCH="i386"
-            ;;
-        x86?64*)
-            SYSTEMTAP_TARGET_ARCH="x86_64"
-            ;;
-        arm*)
-            SYSTEMTAP_TARGET_ARCH="arm"
-            ;;
-        powerpc*)
-            SYSTEMTAP_TARGET_ARCH="powerpc"
-            ;;
-        *)
-            ;;
-    esac
-}
-
-if [ $# -lt 2 ]; then
-	usage
-	exit 1
-fi
-
-if [ -z "$BUILDDIR" ]; then
-    echo "Error: Unable to find the BUILDDIR environment variable."
-    echo "Did you forget to source your build system environment setup script?"
-    exit 1
-fi
-
-pushd $PWD
-cd $BUILDDIR
-BITBAKE_VARS=`bitbake -e virtual/kernel`
-popd
-
-STAGING_BINDIR_TOOLCHAIN=$(echo "$BITBAKE_VARS" | grep ^STAGING_BINDIR_TOOLCHAIN \
-  | cut -d '=' -f2 | cut -d '"' -f2)
-STAGING_BINDIR_TOOLPREFIX=$(echo "$BITBAKE_VARS" | grep ^TARGET_PREFIX \
-  | cut -d '=' -f2 | cut -d '"' -f2)
-TARGET_ARCH=$(echo "$BITBAKE_VARS" | grep ^TRANSLATED_TARGET_ARCH \
-  | cut -d '=' -f2 | cut -d '"' -f2)
-TARGET_KERNEL_BUILDDIR=$(echo "$BITBAKE_VARS" | grep ^B= \
-  | cut -d '=' -f2 | cut -d '"' -f2)
-
-# Build and populate the recipe-sysroot-native with systemtap-native
-pushd $PWD
-cd $BUILDDIR
-BITBAKE_VARS=`bitbake -e systemtap-native`
-popd
-SYSTEMTAP_HOST_INSTALLDIR=$(echo "$BITBAKE_VARS" | grep ^STAGING_DIR_NATIVE \
-  | cut -d '=' -f2 | cut -d '"' -f2)
-
-systemtap_target_arch "$TARGET_ARCH"
-
-if [ ! -d $TARGET_KERNEL_BUILDDIR ] ||
-   [ ! -f $TARGET_KERNEL_BUILDDIR/vmlinux ]; then
-    echo -e "\nError: No target kernel build found."
-    echo -e "Did you forget to create a local build of your image?"
-    setup_usage
-    exit 1
-fi
-
-if [ ! -f $SYSTEMTAP_HOST_INSTALLDIR/usr/bin/stap ]; then
-    echo -e "\nError: Native (host) systemtap not found."
-    echo -e "Did you accidentally build a local non-sdk image? (or forget to"
-    echo -e "add 'tools-profile' to EXTRA_IMAGE_FEATURES in your local.conf)?"
-    echo -e "You can also: bitbake -c addto_recipe_sysroot systemtap-native"
-    setup_usage
-    exit 1
-fi
-
-target_user_hostname="$1"
-full_script_name="$2"
-script_name=$(basename "$2")
-script_base=${script_name%.*}
-shift 2
-
-${SYSTEMTAP_HOST_INSTALLDIR}/usr/bin/stap \
-  -a ${SYSTEMTAP_TARGET_ARCH} \
-  -B CROSS_COMPILE="${STAGING_BINDIR_TOOLCHAIN}/${STAGING_BINDIR_TOOLPREFIX}" \
-  -r ${TARGET_KERNEL_BUILDDIR} \
-  -I ${SYSTEMTAP_HOST_INSTALLDIR}/usr/share/systemtap/tapset \
-  -R ${SYSTEMTAP_HOST_INSTALLDIR}/usr/share/systemtap/runtime \
-  --remote=$target_user_hostname \
-  -m $script_base \
-   $full_script_name "$@"
-
-exit 0
+import sys
+import re
+import subprocess
+import os
+import optparse
+
+class Stap(object):
+    def __init__(self, script, module, remote):
+        self.script = script
+        self.module = module
+        self.remote = remote
+        self.stap = None
+        self.sysroot = None
+        self.runtime = None
+        self.tapset = None
+        self.arch = None
+        self.cross_compile = None
+        self.kernel_release = None
+        self.target_path = None
+        self.target_ld_library_path = None
+
+        if not self.remote:
+            if not self.module:
+                # derive module name from script
+                self.module = os.path.basename(self.script)
+                if self.module[-4:] == ".stp":
+                    self.module = self.module[:-4]
+                    # replace - if any with _
+                    self.module = self.module.replace("-", "_")
+
+    def command(self, args):
+        ret = []
+        ret.append(self.stap)
+
+        if self.remote:
+            ret.append("--remote")
+            ret.append(self.remote)
+        else:
+            ret.append("-p4")
+            ret.append("-m")
+            ret.append(self.module)
+
+        ret.append("-a")
+        ret.append(self.arch)
+
+        ret.append("-B")
+        ret.append("CROSS_COMPILE=" + self.cross_compile)
+
+        ret.append("-r")
+        ret.append(self.kernel_release)
+
+        ret.append("-I")
+        ret.append(self.tapset)
+
+        ret.append("-R")
+        ret.append(self.runtime)
+
+        if self.sysroot:
+            ret.append("--sysroot")
+            ret.append(self.sysroot)
+
+            ret.append("--sysenv=PATH=" + self.target_path)
+            ret.append("--sysenv=LD_LIBRARY_PATH=" + self.target_ld_library_path)
+
+        ret = ret + args
+
+        ret.append(self.script)
+        return ret
+
+    def additional_environment(self):
+        ret = {}
+        ret["SYSTEMTAP_DEBUGINFO_PATH"] = "+:.debug:build"
+        return ret
+
+    def environment(self):
+        ret = os.environ.copy()
+        additional = self.additional_environment()
+        for e in additional:
+            ret[e] = additional[e]
+        return ret
+
+    def display_command(self, args):
+        additional_env = self.additional_environment()
+        command = self.command(args)
+
+        print("#!/bin/sh")
+        for e in additional_env:
+            print("export %s=\"%s\"" % (e, additional_env[e]))
+        print(" ".join(command))
+
+class BitbakeEnvInvocationException(Exception):
+    def __init__(self, message):
+        self.message = message
+
+class BitbakeEnv(object):
+    BITBAKE="bitbake"
+
+    def __init__(self, package):
+        self.package = package
+        self.cmd = BitbakeEnv.BITBAKE + " -e " + self.package
+        self.popen = subprocess.Popen(self.cmd, shell=True,
+                                      stdout=subprocess.PIPE,
+                                      stderr=subprocess.STDOUT)
+        self.__lines = self.popen.stdout.readlines()
+        self.popen.wait()
+
+        self.lines = []
+        for line in self.__lines:
+                self.lines.append(line.decode('utf-8'))
+
+    def get_vars(self, vars):
+        if self.popen.returncode:
+            raise BitbakeEnvInvocationException(
+                "\nFailed to execute '" + self.cmd +
+                "' with the following message:\n" +
+                ''.join(self.lines))
+
+        search_patterns = []
+        retdict = {}
+        for var in vars:
+            # regular not exported variable
+            rexpr = "^" + var + "=\"(.*)\""
+            re_compiled = re.compile(rexpr)
+            search_patterns.append((var, re_compiled))
+
+            # exported variable
+            rexpr = "^export " + var + "=\"(.*)\""
+            re_compiled = re.compile(rexpr)
+            search_patterns.append((var, re_compiled))
+
+            for line in self.lines:
+                for var, rexpr in search_patterns:
+                    m = rexpr.match(line)
+                    if m:
+                        value = m.group(1)
+                        retdict[var] = value
+
+        # fill variables values in order how they were requested
+        ret = []
+        for var in vars:
+            ret.append(retdict.get(var))
+
+        # if it is single value list return it as scalar, not the list
+        if len(ret) == 1:
+            ret = ret[0]
+
+        return ret
+
+class ParamDiscovery(object):
+    SYMBOLS_CHECK_MESSAGE = """
+WARNING: image '%s' does not have dbg-pkgs IMAGE_FEATURES enabled and no
+"image-combined-dbg" in inherited classes is specified. As result the image
+does not have symbols for user-land processes DWARF based probes. Consider
+adding 'dbg-pkgs' to EXTRA_IMAGE_FEATURES or adding "image-combined-dbg" to
+USER_CLASSES. I.e add this line 'USER_CLASSES += "image-combined-dbg"' to
+local.conf file.
+
+Or you may use IMAGE_GEN_DEBUGFS="1" option, and then after build you need
+recombine/unpack image and image-dbg tarballs and pass resulting dir location
+with --sysroot option.
+"""
+
+    def __init__(self, image):
+        self.image = image
+
+        self.image_rootfs = None
+        self.image_features = None
+        self.image_gen_debugfs = None
+        self.inherit = None
+        self.base_bindir = None
+        self.base_sbindir = None
+        self.base_libdir = None
+        self.bindir = None
+        self.sbindir = None
+        self.libdir = None
+
+        self.staging_bindir_toolchain = None
+        self.target_prefix = None
+        self.target_arch = None
+        self.target_kernel_builddir = None
+
+        self.staging_dir_native = None
+
+        self.image_combined_dbg = False
+
+    def discover(self):
+        if self.image:
+            benv_image = BitbakeEnv(self.image)
+            (self.image_rootfs,
+             self.image_features,
+             self.image_gen_debugfs,
+             self.inherit,
+             self.base_bindir,
+             self.base_sbindir,
+             self.base_libdir,
+             self.bindir,
+             self.sbindir,
+             self.libdir
+            ) = benv_image.get_vars(
+                 ("IMAGE_ROOTFS",
+                  "IMAGE_FEATURES",
+                  "IMAGE_GEN_DEBUGFS",
+                  "INHERIT",
+                  "base_bindir",
+                  "base_sbindir",
+                  "base_libdir",
+                  "bindir",
+                  "sbindir",
+                  "libdir"
+                  ))
+
+        benv_kernel = BitbakeEnv("virtual/kernel")
+        (self.staging_bindir_toolchain,
+         self.target_prefix,
+         self.target_arch,
+         self.target_kernel_builddir
+        ) = benv_kernel.get_vars(
+             ("STAGING_BINDIR_TOOLCHAIN",
+              "TARGET_PREFIX",
+              "TRANSLATED_TARGET_ARCH",
+              "B"
+            ))
+
+        benv_systemtap = BitbakeEnv("systemtap-native")
+        (self.staging_dir_native
+        ) = benv_systemtap.get_vars(["STAGING_DIR_NATIVE"])
+
+        if self.inherit:
+            if "image-combined-dbg" in self.inherit.split():
+                self.image_combined_dbg = True
+
+    def check(self, sysroot_option):
+        ret = True
+        if self.image_rootfs:
+            sysroot = self.image_rootfs
+            if not os.path.isdir(self.image_rootfs):
+                print("ERROR: Cannot find '" + sysroot +
+                      "' directory. Was '" + self.image + "' image built?")
+                ret = False
+
+        stap = self.staging_dir_native + "/usr/bin/stap"
+        if not os.path.isfile(stap):
+            print("ERROR: Cannot find '" + stap +
+                  "'. Was 'systemtap-native' built?")
+            ret = False
+
+        if not os.path.isdir(self.target_kernel_builddir):
+            print("ERROR: Cannot find '" + self.target_kernel_builddir +
+                  "' directory. Was 'kernel/virtual' built?")
+            ret = False
+
+        if not sysroot_option and self.image_rootfs:
+            dbg_pkgs_found = False
+
+            if self.image_features:
+                image_features = self.image_features.split()
+                if "dbg-pkgs" in image_features:
+                    dbg_pkgs_found = True
+
+            if not dbg_pkgs_found \
+               and not self.image_combined_dbg:
+                print(ParamDiscovery.SYMBOLS_CHECK_MESSAGE % (self.image))
+
+        if not ret:
+            print("")
+
+        return ret
+
+    def __map_systemtap_arch(self):
+        a = self.target_arch
+        ret = a
+        if   re.match('(athlon|x86.64)$', a):
+            ret = 'x86_64'
+        elif re.match('i.86$', a):
+            ret = 'i386'
+        elif re.match('arm$', a):
+            ret = 'arm'
+        elif re.match('aarch64$', a):
+            ret = 'arm64'
+        elif re.match('mips(isa|)(32|64|)(r6|)(el|)$', a):
+            ret = 'mips'
+        elif re.match('p(pc|owerpc)(|64)', a):
+            ret = 'powerpc'
+        return ret
+
+    def fill_stap(self, stap):
+        stap.stap = self.staging_dir_native + "/usr/bin/stap"
+        if not stap.sysroot:
+            if self.image_rootfs:
+                if self.image_combined_dbg:
+                    stap.sysroot = self.image_rootfs + "-dbg"
+                else:
+                    stap.sysroot = self.image_rootfs
+        stap.runtime = self.staging_dir_native + "/usr/share/systemtap/runtime"
+        stap.tapset = self.staging_dir_native + "/usr/share/systemtap/tapset"
+        stap.arch = self.__map_systemtap_arch()
+        stap.cross_compile = self.staging_bindir_toolchain + "/" + \
+                             self.target_prefix
+        stap.kernel_release = self.target_kernel_builddir
+
+        # do we have standard that tells in which order these need to appear
+        target_path = []
+        if self.sbindir:
+            target_path.append(self.sbindir)
+        if self.bindir:
+            target_path.append(self.bindir)
+        if self.base_sbindir:
+            target_path.append(self.base_sbindir)
+        if self.base_bindir:
+            target_path.append(self.base_bindir)
+        stap.target_path = ":".join(target_path)
+
+        target_ld_library_path = []
+        if self.libdir:
+            target_ld_library_path.append(self.libdir)
+        if self.base_libdir:
+            target_ld_library_path.append(self.base_libdir)
+        stap.target_ld_library_path = ":".join(target_ld_library_path)
+
+
+def main():
+    usage = """usage: %prog -s <systemtap-script> [options] [-- [systemtap options]]
+
+%prog cross compile given SystemTap script against given image, kernel
+
+It needs to run in environtment set for bitbake - it uses bitbake -e
+invocations to retrieve information to construct proper stap cross build
+invocation arguments. It assumes that systemtap-native is built in given
+bitbake workspace.
+
+Anything after -- option is passed directly to stap.
+
+Legacy script invocation style supported but depreciated:
+  %prog <user@hostname> <sytemtap-script> [systemtap options]
+
+To enable most out of systemtap the following site.conf or local.conf
+configuration is recommended:
+
+# enables symbol + target binaries rootfs-dbg in workspace
+IMAGE_GEN_DEBUGFS = "1"
+IMAGE_FSTYPES_DEBUGFS = "tar.bz2"
+USER_CLASSES += "image-combined-dbg"
+
+# enables kernel debug symbols
+KERNEL_EXTRA_FEATURES_append = " features/debug/debug-kernel.scc"
+
+# minimal, just run-time systemtap configuration in target image
+PACKAGECONFIG_pn-systemtap = "monitor"
+
+# add systemtap run-time into target image if it is not there yet
+IMAGE_INSTALL_append = " systemtap"
+"""
+    option_parser = optparse.OptionParser(usage=usage)
+
+    option_parser.add_option("-s", "--script", dest="script",
+                             help="specify input script FILE name",
+                             metavar="FILE")
+
+    option_parser.add_option("-i", "--image", dest="image",
+                             help="specify image name for which script should be compiled")
+
+    option_parser.add_option("-r", "--remote", dest="remote",
+                             help="specify username@hostname of remote target to run script "
+                             "optional, it assumes that remote target can be accessed through ssh")
+
+    option_parser.add_option("-m", "--module", dest="module",
+                             help="specify module name, optional, has effect only if --remote is not used, "
+                             "if not specified module name will be derived from passed script name")
+
+    option_parser.add_option("-y", "--sysroot", dest="sysroot",
+                             help="explicitely specify image sysroot location. May need to use it in case "
+                             "when IMAGE_GEN_DEBUGFS=\"1\" option is used and recombined with symbols "
+                             "in different location",
+                             metavar="DIR")
+
+    option_parser.add_option("-o", "--out", dest="out",
+                             action="store_true",
+                             help="output shell script that equvivalent invocation of this script with "
+                             "given set of arguments, in given bitbake environment. It could be stored in "
+                             "separate shell script and could be repeated without incuring bitbake -e "
+                             "invocation overhead",
+                             default=False)
+
+    option_parser.add_option("-d", "--debug", dest="debug",
+                             action="store_true",
+                             help="enable debug output. Use this option to see resulting stap invocation",
+                             default=False)
+
+    # is invocation follow syntax from orignal crosstap shell script
+    legacy_args = False
+
+    # check if we called the legacy way
+    if len(sys.argv) >= 3:
+        if sys.argv[1].find("@") != -1 and os.path.exists(sys.argv[2]):
+            legacy_args = True
+
+            # fill options values for legacy invocation case
+            options = optparse.Values
+            options.script = sys.argv[2]
+            options.remote = sys.argv[1]
+            options.image = None
+            options.module = None
+            options.sysroot = None
+            options.out = None
+            options.debug = None
+            remaining_args = sys.argv[3:]
+
+    if not legacy_args:
+        (options, remaining_args) = option_parser.parse_args()
+
+    if not options.script or not os.path.exists(options.script):
+        print("'-s FILE' option is missing\n")
+        option_parser.print_help()
+    else:
+        stap = Stap(options.script, options.module, options.remote)
+        discovery = ParamDiscovery(options.image)
+        discovery.discover()
+        if not discovery.check(options.sysroot):
+            option_parser.print_help()
+        else:
+            stap.sysroot = options.sysroot
+            discovery.fill_stap(stap)
+
+            if options.out:
+                stap.display_command(remaining_args)
+            else:
+                cmd = stap.command(remaining_args)
+                env = stap.environment()
+
+                if options.debug:
+                    print(" ".join(cmd))
+
+                os.execve(cmd[0], cmd, env)
+
+main()