[Asterisk-code-review] Enable retention of process maps when it might be useful. (testsuite[master])

Corey Farrell asteriskteam at digium.com
Wed Jun 3 10:11:34 CDT 2015


Corey Farrell has uploaded a new change for review.

  https://gerrit.asterisk.org/576

Change subject: Enable retention of process maps when it might be useful.
......................................................................

Enable retention of process maps when it might be useful.

This change has no purpose for current versions of Asterisk.
They do not call dlclose during shutdown, so backtraces from
the testsuite normally should not contain unresolvable symbols.

This should be used for all testing of the module loader rewrite:
https://gerrit.asterisk.org/557

Final Commit Message:
---------------------
Make a copy of /proc/$PID/maps for each Asterisk process when it
is fully booted.  This file is then copied to testsuite logs if
valgrind is enabled or if core dumps were detected.

The process maps file is only copied for Asterisk 14 or above.
Previous versions of Asterisk never dlclosed modules during the
testsuite, so the maps file is unneeded.  The file is only copied
when it could be useful since it can easily be 400k.  Even without
core dumps or valgrind this can add up very quickly if tmp is not
cleaned up after every test.

A find_symbol.py script is added to allow retrieving symbol
information using a proc-maps.txt and an unresolvable pointer
address from the backtrace.

Change-Id: Ie67750892cbe0e754061c90adedf682840b069b0
---
A contrib/scripts/find_symbol.py
M lib/python/asterisk/asterisk.py
M runtests.py
3 files changed, 107 insertions(+), 16 deletions(-)


  git pull ssh://gerrit.asterisk.org:29418/testsuite refs/changes/76/576/1

diff --git a/contrib/scripts/find_symbol.py b/contrib/scripts/find_symbol.py
new file mode 100755
index 0000000..4982dcc
--- /dev/null
+++ b/contrib/scripts/find_symbol.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+"""Find a symbol from the address of an unloaded module.
+
+ This script uses a process map to determine which module
+ an unloaded address was from, uses the information to run
+ addr2line.
+
+ See http://www.asterisk.org for more information about
+ the Asterisk project. Please do not directly contact
+ any of the maintainers of this project for assistance;
+ the project provides a web site, mailing lists and IRC
+ channels for your use.
+
+ This program is free software, distributed under the terms of
+ the GNU General Public License Version 2. See the LICENSE file
+ at the top of the source tree.
+
+ Copyright (C) 2015, CFWare, LLC
+ Corey Farrell <git at cfware.com>
+"""
+
+import sys
+import os
+
+from optparse import OptionParser
+
+
+def main(argv=None):
+    """Main entry point for the script"""
+
+    if argv is None:
+        argv = sys.argv
+
+    parser = OptionParser()
+    parser.add_option("-m", "--process-map", action="store", type="string",
+                      dest="mappath", default=None,
+                      help="The full path to the copy of /proc/self/maps.")
+    parser.add_option("-a", "--address", action="store", type="string",
+                      dest="address", default=None,
+                      help="A pointer from an unloaded module.")
+
+    (options, args) = parser.parse_args(argv)
+    if not options.mappath or not options.address:
+        print >>sys.stderr, "Both options are required."
+        return -1
+
+    if not os.path.isfile(options.mappath):
+        print >>sys.stderr, "File not found: %s" % options.mappath
+        return -1
+
+    find_addr = int(options.address, 16)
+    with open(options.mappath, 'r') as mapfile:
+        for line in mapfile:
+            tokens = line.strip().replace('  ', ' ').split()
+            addr = tokens[0].split('-')
+            addr[0] = int(addr[0], 16)
+            addr[1] = int(addr[1], 16)
+
+            # TODO: verify that '>= and <' is the correct range
+            if find_addr >= addr[0] and find_addr < addr[1]:
+                offset = find_addr - addr[0]
+                if len(tokens) < 6 or tokens[5].startswith('['):
+                    print "This address is not associated with a module"
+                    return 0
+                module = tokens[5]
+                print "Found symbol at offset 0x%x of %s" % (offset, module)
+                os.execl('/usr/bin/addr2line',
+                         'addr2line', '-spfe', module, "0x%x" % offset)
+
+                # This location should be unreachable..
+                return -1
+
+    print >>sys.stderr, \
+        "No range contained the address %s, verify you have the correct process map." % \
+        options.address
+
+    return -1
+
+
+if __name__ == "__main__":
+    sys.exit(main(sys.argv))
diff --git a/lib/python/asterisk/asterisk.py b/lib/python/asterisk/asterisk.py
index 097d227..d4f13d3 100755
--- a/lib/python/asterisk/asterisk.py
+++ b/lib/python/asterisk/asterisk.py
@@ -325,6 +325,13 @@
 
             if "Asterisk has fully booted" in cli_command.output:
                 msg = "Successfully started Asterisk %s" % self.host
+                # Asterisk does not dlclose modules at shutdown in previous
+                # versions, do not waste the space for this copy in those versions.
+                if self.ast_version >= AsteriskVersion("14.0.0"):
+                    shutil.copyfile(
+                        os.path.join('/proc', '%d' % self.process.pid, 'maps'),
+                        self.get_path('astlogdir', 'proc-maps.txt')
+                    )
                 self._start_deferred.callback(msg)
             else:
                 LOGGER.debug("Asterisk core waitfullybooted failed " +
diff --git a/runtests.py b/runtests.py
index 5bfaff6..68c103d 100755
--- a/runtests.py
+++ b/runtests.py
@@ -52,6 +52,7 @@
         self.can_run = False
         self.did_run = False
         self.time = 0.0
+        self.using_valgrind = False
         self.test_name = test_name
         self.ast_version = ast_version
         self.options = options
@@ -114,15 +115,7 @@
 
             self.passed = (did_pass == self.test_config.expect_pass)
 
-            core_dumps = self._check_for_core()
-            if (len(core_dumps)):
-                if self.passed:
-                    self.stdout_print("Core dumps detected; failing test")
-                    self.passed = False
-                else:
-                    self.stdout_print("Core dumps detected; test was already failed")
-                self._archive_core_dumps(core_dumps)
-
+            self._process_core_dumps()
             self._process_valgrind()
             self._process_ref_debug()
 
@@ -149,7 +142,7 @@
             print "FAILED TO EXECUTE %s, it must exist and be executable" % cmd
         self.time = time.time() - start_time
 
-    def _check_for_core(self):
+    def _process_core_dumps(self):
         core_files = []
 
         contents = os.listdir('.')
@@ -162,10 +155,17 @@
             if item.startswith('core') or item.startswith('vgcore'):
                 core_files.append(os.path.join(self.test_name, item))
 
-        return core_files
+        self.core_files = core_files
+        if not len(self.core_files):
+            return
 
-    def _archive_core_dumps(self, core_dumps):
-        for core in core_dumps:
+        if self.passed:
+            self.stdout_print("Core dumps detected; failing test")
+            self.passed = False
+        else:
+            self.stdout_print("Core dumps detected; test was already failed")
+
+        for core in core_files:
             if not os.path.exists(core):
                 print "Unable to find core dump file %s, skipping" % core
                 continue
@@ -233,6 +233,7 @@
             if not os.path.exists(valgrind_xml):
                 return
 
+            self.using_valgrind = True
             dom = ET.parse(valgrind_xml)
             xslt = ET.parse('contrib/valgrind/summary-lines.xsl')
             transform = ET.XSLT(xslt)
@@ -324,9 +325,11 @@
             ast_dir = "%s/ast%d/var/log/asterisk" % (run_dir, i)
             dest_dir = os.path.join(archive_dir,
                                     'ast%d/var/log/asterisk' % i)
-            self._archive_files(ast_dir, dest_dir,
-                                'messages.txt', 'full.txt', 'mmlog',
-                                'valgrind.xml', 'valgrind-summary.txt')
+            logfiles = ['messages.txt', 'full.txt', 'mmlog',
+                        'valgrind.xml', 'valgrind-summary.txt']
+            if len(self.core_files) or self.using_valgrind:
+                logfiles.append('proc-maps.txt')
+            self._archive_files(ast_dir, dest_dir, *logfiles)
             i += 1
 
     def _archive_pcap_dump(self, run_dir, archive_dir):

-- 
To view, visit https://gerrit.asterisk.org/576
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ie67750892cbe0e754061c90adedf682840b069b0
Gerrit-PatchSet: 1
Gerrit-Project: testsuite
Gerrit-Branch: master
Gerrit-Owner: Corey Farrell <git at cfware.com>



More information about the asterisk-code-review mailing list