[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Minios-devel] [UNIKRAFT PATCH v2 5/8] support/scripts: fetch trace data from running Unikraft



Fetch tracebuffer, key-values and tracepoint definitions using objcopy
and gdb.

This patch introduces uk-gdb.py script, which is a gdb-extension
providing handy helpers. Currently, it is only tracepoints related
stuff, but possibilities are going beyond that.

The uk-gdb.py can be loaded into gdb manually (via "source" command)
or using gdb's auto-load feature. Gdb looks for a file named
objfile-gdb.py, where objfile is the object file's real name.

To achieve that uk-gdb.py is copied into the build directory with a
little modification - hardcode the absolute path to the Unikraft's
scripts directory.

The trace.py is a tool for analyzing traces. Currently, it does only
the basic stuff: fetches tracing data from a running instance (can run
gdb for you) and prints out parsed information (in the next
patch). But it will eventually be able to do more. For example
statistics of most frequently occurred events.

Signed-off-by: Yuri Volchkov <yuri.volchkov@xxxxxxxxx>
---
 Makefile                             |  12 ++-
 support/scripts/uk-gdb.py            | 122 +++++++++++++++++++++++++++
 support/scripts/uk_trace/__init__.py |   0
 support/scripts/uk_trace/parse.py    |  57 +++++++++++++
 support/scripts/uk_trace/trace.py    | 103 ++++++++++++++++++++++
 5 files changed, 293 insertions(+), 1 deletion(-)
 create mode 100644 support/scripts/uk-gdb.py
 create mode 100644 support/scripts/uk_trace/__init__.py
 create mode 100644 support/scripts/uk_trace/parse.py
 create mode 100755 support/scripts/uk_trace/trace.py

diff --git a/Makefile b/Makefile
index c0492920..e423be8d 100644
--- a/Makefile
+++ b/Makefile
@@ -534,8 +534,18 @@ libs: $(UK_ALIBS) $(UK_ALIBS-y) $(UK_OLIBS) $(UK_OLIBS-y)
 
 images: $(UK_IMAGES) $(UK_IMAGES-y)
 
-all: images
+GDB_HELPER_LINKS := $(addsuffix  .dbg-gdb.py,$(UK_IMAGES-y) $(UK_IMAGES))
+$(GDB_HELPER_LINKS):
+       $(call verbose_cmd,LN,$(notdir $@), ln -sf uk-gdb.py $@)
 
+SCRIPTS_DIR_BACKSLASHED = $(subst /,\/,$(SCRIPTS_DIR))
+$(BUILD_DIR)/uk-gdb.py: $(SCRIPTS_DIR)/uk-gdb.py
+       $(call verbose_cmd,GEN,$(notdir $@), \
+       sed '/scripts_dir = / 
s/os.path.dirname(os.path.realpath(__file__))/"$(SCRIPTS_DIR_BACKSLASHED)"/g' 
$^ > $@)
+
+gdb_helpers: $(GDB_HELPER_LINKS) $(BUILD_DIR)/uk-gdb.py
+
+all: images gdb_helpers
 
################################################################################
 # Cleanup rules
 
################################################################################
diff --git a/support/scripts/uk-gdb.py b/support/scripts/uk-gdb.py
new file mode 100644
index 00000000..e4b0038d
--- /dev/null
+++ b/support/scripts/uk-gdb.py
@@ -0,0 +1,122 @@
+#!/user/bin/env python3
+# SPDX-License-Identifier: BSD-3-Clause */
+#
+# Authors: Yuri Volchkov <yuri.volchkov@xxxxxxxxx>
+#
+# Copyright (c) 2019, NEC Laboratories Europe GmbH, NEC Corporation.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+#    contributors may be used to endorse or promote products derived from
+#    this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY.
+
+import gdb
+import pickle
+import os, sys
+import tempfile, shutil
+
+scripts_dir = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(scripts_dir)
+
+import uk_trace.parse as parse
+
+type_char = gdb.lookup_type('char')
+type_void = gdb.lookup_type('void')
+
+PTR_SIZE = type_void.pointer().sizeof
+
+def get_trace_buffer():
+    inf = gdb.selected_inferior()
+
+    try:
+        trace_buff = gdb.parse_and_eval('uk_trace_buffer')
+        trace_buff_size = trace_buff.type.sizeof
+        trace_buff_addr = int(trace_buff.address)
+        trace_buff_writep = int(gdb.parse_and_eval('uk_trace_buffer_writep'))
+    except gdb.error:
+        gdb.write("Error getting the trace buffer. Is tracing enabled?\n")
+        raise gdb.error
+
+    if (trace_buff_writep == 0):
+        # This can happen as effect of compile optimization if none of
+        # tracepoints were called
+        used = 0
+    else:
+        used = trace_buff_writep - trace_buff_addr
+
+    return bytes(inf.read_memory(trace_buff_addr, used))
+
+def save_traces(out):
+    elf = gdb.current_progspace().filename
+
+    pickler = pickle.Pickler(out)
+
+    # keyvals need to go first, because they have format_version
+    # key. Even if the format is changed we must guarantee that at
+    # least keyvals are always stored first. However, ideally, next
+    # versions should just have modifications at the very end to keep
+    # compatibility with previously collected data.
+    pickler.dump(parse.get_keyvals(elf))
+    pickler.dump(elf)
+    pickler.dump(PTR_SIZE)
+    # We are saving raw trace buffer here. Another option is to pickle
+    # already parsed samples. But in the chosen case it is a lot
+    # easier to debug the parser, because python in gdb is not very
+    # convenient for development.
+    pickler.dump(parse.get_tp_sections(elf))
+    pickler.dump(get_trace_buffer())
+
+class uk(gdb.Command):
+    def __init__(self):
+        gdb.Command.__init__(self, 'uk',
+                             gdb.COMMAND_USER, gdb.COMPLETE_COMMAND, True)
+
+class uk_trace(gdb.Command):
+    def __init__(self):
+        gdb.Command.__init__(self, 'uk trace',
+                             gdb.COMMAND_USER, gdb.COMPLETE_COMMAND, True)
+    def invoke(self, arg, from_tty):
+        # TODO
+        pass
+
+class uk_trace_save(gdb.Command):
+    def __init__(self):
+        gdb.Command.__init__(self, 'uk trace save',
+                             gdb.COMMAND_USER, gdb.COMPLETE_COMMAND)
+    def invoke(self, arg, from_tty):
+        if not arg:
+            gdb.write('Missing argument. Usage: uk trace save <filename>\n')
+            return
+
+        gdb.write('Saving traces to %s ...\n' % arg)
+
+        with tempfile.NamedTemporaryFile() as out:
+            save_traces(out)
+            out.flush()
+            shutil.copyfile(out.name, arg)
+
+uk()
+uk_trace()
+uk_trace_save()
diff --git a/support/scripts/uk_trace/__init__.py 
b/support/scripts/uk_trace/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/support/scripts/uk_trace/parse.py 
b/support/scripts/uk_trace/parse.py
new file mode 100644
index 00000000..407f47e3
--- /dev/null
+++ b/support/scripts/uk_trace/parse.py
@@ -0,0 +1,57 @@
+# SPDX-License-Identifier: BSD-3-Clause */
+#
+# Authors: Yuri Volchkov <yuri.volchkov@xxxxxxxxx>
+#
+# Copyright (c) 2019, NEC Laboratories Europe GmbH, NEC Corporation.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+#    contributors may be used to endorse or promote products derived from
+#    this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY.
+
+import subprocess
+import re
+import tempfile
+
+def get_tp_sections(elf):
+    f = tempfile.NamedTemporaryFile()
+    objcopy_cmd  = 'objcopy -O binary %s ' % elf
+    objcopy_cmd += '--only-section=.uk_tracepoints_list ' + f.name
+    objcopy_cmd = objcopy_cmd.split()
+    subprocess.check_call(objcopy_cmd)
+    return f.read()
+
+def get_keyvals(elf):
+    readelf_cmd = 'readelf -p .uk_trace_keyvals %s' % elf
+    readelf_cmd = readelf_cmd.split()
+    raw_data = subprocess.check_output(readelf_cmd).decode()
+    filtered = re.findall(r'^\s*\[ *\d+\]\s+(.+) = (.+)$', raw_data,
+                          re.MULTILINE)
+
+    ret = dict()
+    for key,val in filtered:
+        ret[key] = val
+
+    return ret
diff --git a/support/scripts/uk_trace/trace.py 
b/support/scripts/uk_trace/trace.py
new file mode 100755
index 00000000..10c985e5
--- /dev/null
+++ b/support/scripts/uk_trace/trace.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: BSD-3-Clause */
+#
+# Authors: Yuri Volchkov <yuri.volchkov@xxxxxxxxx>
+#
+# Copyright (c) 2019, NEC Laboratories Europe GmbH, NEC Corporation.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+#    contributors may be used to endorse or promote products derived from
+#    this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY.
+
+import click
+import os, sys
+import pickle
+import subprocess
+
+import parse
+
+@click.group()
+def cli():
+    pass
+
+def parse_tf(trace_file):
+    try:
+        with open(trace_file, 'rb') as tf:
+            unpickler = pickle.Unpickler(tf)
+
+            keyvals = unpickler.load()
+            elf = unpickler.load()
+            ptr_size = unpickler.load()
+            tp_defs = unpickler.load()
+            trace_buff = unpickler.load()
+    except EOFError:
+        print("Unexpected end of trace file", file=sys.stderr)
+        quit(-1)
+    except Exception as inst:
+        print("Problem occurred during reading the tracefile: %s" % str(inst))
+        quit(-1)
+
+    return parse.sample_parser(keyvals, tp_defs, trace_buff, ptr_size)
+
+@cli.command()
+@click.argument('uk_img', type=click.Path(exists=True))
+@click.option('--out', '-o', type=click.Path(),
+              default='tracefile', show_default=True,
+              help='Output binary file')
+@click.option('--remote', '-r', type=click.STRING,
+              default=':1234', show_default=True,
+              help='How to connect to the gdb session '+
+              '(parameters for "target remote" command)')
+@click.option('--verbose', is_flag=True, default=False)
+def fetch(uk_img, out, remote, verbose):
+    """Fetch binary trace file from Unikraft (using gdb)"""
+
+    if os.path.exists(out):
+        os.remove(out)
+
+    helper_path = os.path.abspath(uk_img) + '-gdb.py'
+    gdb_cmd = ['gdb', '-nh', '-batch',
+               click.format_filename(uk_img),
+               '-iex', 'add-auto-load-safe-path ' + helper_path,
+               '-ex', 'target remote ' + remote,
+               '-ex', 'uk trace save ' + out
+    ]
+
+    proc = subprocess.Popen(gdb_cmd,
+                            stdout=subprocess.PIPE,
+                            stderr=subprocess.STDOUT
+    )
+    _stdout, _ = proc.communicate()
+    _stdout = _stdout.decode()
+    if proc.returncode or not os.path.exists(out):
+        print(_stdout)
+        sys.exit(1)
+    if verbose:
+        print(_stdout)
+
+
+if __name__ == '__main__':
+    cli()
-- 
2.19.2


_______________________________________________
Minios-devel mailing list
Minios-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/minios-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.