[Lldb-commits] [lldb] r236956 - Add support for ./dotest.py --channel and --log-success

Vince Harron vince at nethacker.com
Sun May 10 08:22:09 PDT 2015


Author: vharron
Date: Sun May 10 10:22:09 2015
New Revision: 236956

URL: http://llvm.org/viewvc/llvm-project?rev=236956&view=rev
Log:
Add support for ./dotest.py --channel and --log-success

Summary:
New dotest options that allow arbitrary log channels and
categories to be enabled.  Also enables logging for locally run
debug servers.
    
Log messages are separated into separate files per test case.
(this makes it possible to log in dosep runs)
    
These new log files are stored side-by-side with trace files in the
session directory.
    
These files are deleted by default if the test run is successful.
    
If --log-success is specified, even successful logs are retained.
    
--log-success is useful for creating reference log files.
    
Test Plan:
add '--channel "lldb all" --channel "gdb-remote packets" --log-success'
to your dotest options

Tested on OSX and Linux
    
Differential Revision: http://reviews.llvm.org/D9594


Added:
    lldb/trunk/test/lldbtest_config.py
Modified:
    lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
    lldb/trunk/test/dotest.py
    lldb/trunk/test/lldbtest.py
    lldb/trunk/tools/lldb-server/CMakeLists.txt
    lldb/trunk/tools/lldb-server/lldb-gdbserver.cpp

Modified: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp?rev=236956&r1=236955&r2=236956&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp (original)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp Sun May 10 10:22:09 2015
@@ -850,12 +850,21 @@ GDBRemoteCommunication::StartDebugserver
             debugserver_args.AppendArgument(arg_cstr);
         }
         
+#if defined(__APPLE__)
         const char *env_debugserver_log_flags = getenv("LLDB_DEBUGSERVER_LOG_FLAGS");
         if (env_debugserver_log_flags)
         {
             ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-flags=%s", env_debugserver_log_flags);
             debugserver_args.AppendArgument(arg_cstr);
         }
+#else
+        const char *env_debugserver_log_channels = getenv("LLDB_SERVER_LOG_CHANNELS");
+        if (env_debugserver_log_channels)
+        {
+            ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-channels=%s", env_debugserver_log_channels);
+            debugserver_args.AppendArgument(arg_cstr);
+        }
+#endif
 
         // Add additional args, starting with LLDB_DEBUGSERVER_EXTRA_ARG_1 until an env var doesn't come back.
         uint32_t env_var_index = 1;

Modified: lldb/trunk/test/dotest.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/dotest.py?rev=236956&r1=236955&r2=236956&view=diff
==============================================================================
--- lldb/trunk/test/dotest.py (original)
+++ lldb/trunk/test/dotest.py Sun May 10 10:22:09 2015
@@ -32,6 +32,7 @@ import textwrap
 import time
 import inspect
 import unittest2
+import lldbtest_config
 
 if sys.version_info >= (2, 7):
     argparse = __import__('argparse')
@@ -337,7 +338,36 @@ notify the directory containing the sess
 In case there is any test failure/error, a similar message is appended at the
 end of the stderr output for your convenience.
 
-Environment variables related to loggings:
+ENABLING LOGS FROM TESTS
+
+Option 1:
+
+Writing logs into different files per test case::
+
+This option is particularly useful when multiple dotest instances are created
+by dosep.py
+
+$ ./dotest.py --channel "lldb all"
+
+$ ./dotest.py --channel "lldb all" --channel "gdb-remote packets"
+
+These log files are written to:
+
+<session-dir>/<test-id>.log (logs from lldb host process)
+<session-dir>/<test-id>-server.log (logs from debugserver/lldb-server)
+<session-dir>/<test-id>-trace-<test-result>.log (console logs)
+
+By default, logs from successful runs are deleted.  Use the --log-success flag
+to create reference logs for debugging.
+
+$ ./dotest.py --log-success
+
+Option 2: (DEPRECATED)
+
+The following options can only enable logs from the host lldb process.
+Only categories from the "lldb" or "gdb-remote" channels can be enabled
+They also do not automatically enable logs in locally running debug servers.
+Also, logs from all test case are written into each log file
 
 o LLDB_LOG: if defined, specifies the log file pathname for the 'lldb' subsystem
   with a default option of 'event process' if LLDB_LOG_OPTION is not defined.
@@ -522,6 +552,8 @@ def parseOptionsAndInitTestdirs():
     group.add_argument('-x', metavar='breakpoint-spec', help='Specify the breakpoint specification for the benchmark executable')
     group.add_argument('-y', type=int, metavar='count', help="Specify the iteration count used to collect our benchmarks. An example is the number of times to do 'thread step-over' to measure stepping speed.")
     group.add_argument('-#', type=int, metavar='sharp', dest='sharp', help='Repeat the test suite for a specified number of times')
+    group.add_argument('--channel', metavar='channel', dest='channels', action='append', help=textwrap.dedent("Specify the log channels (and optional categories) e.g. 'lldb all' or 'gdb-remote packets' if no categories are specified, 'default' is used"))
+    group.add_argument('--log-success', dest='log_success', action='store_true', help="Leave logs/traces even for successful test runs (useful for creating reference log files during debugging.)")
 
     # Configuration options
     group = parser.add_argument_group('Remote platform options')
@@ -589,6 +621,12 @@ def parseOptionsAndInitTestdirs():
         else:
             compilers = ['clang']
 
+    if args.channels:
+        lldbtest_config.channels = args.channels
+
+    if args.log_success:
+        lldbtest_config.log_success = args.log_success
+
     # Set SDKROOT if we are using an Apple SDK
     if platform_system == 'Darwin' and args.apple_sdk:
         os.environ['SDKROOT'] = commands.getoutput('xcrun --sdk "%s" --show-sdk-path 2> /dev/null' % (args.apple_sdk))

Modified: lldb/trunk/test/lldbtest.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/lldbtest.py?rev=236956&r1=236955&r2=236956&view=diff
==============================================================================
--- lldb/trunk/test/lldbtest.py (original)
+++ lldb/trunk/test/lldbtest.py Sun May 10 10:22:09 2015
@@ -32,6 +32,7 @@ $
 """
 
 import abc
+import glob
 import os, sys, traceback
 import os.path
 import re
@@ -42,6 +43,7 @@ import time
 import types
 import unittest2
 import lldb
+import lldbtest_config
 from _pyio import __metaclass__
 
 # See also dotest.parseOptionsAndInitTestdirs(), where the environment variables
@@ -1284,6 +1286,8 @@ class Base(unittest2.TestCase):
                 for dict in reversed(self.dicts):
                     self.cleanup(dictionary=dict)
 
+        self.disableLogChannelsForCurrentTest()
+
         # Decide whether to dump the session info.
         self.dumpSessionInfo()
 
@@ -1342,7 +1346,33 @@ class Base(unittest2.TestCase):
 
     def getRerunArgs(self):
         return " -f %s.%s" % (self.__class__.__name__, self._testMethodName)
-        
+
+    def getLogBasenameForCurrentTest(self, prefix=None):
+        """
+        returns a partial path that can be used as the beginning of the name of multiple
+        log files pertaining to this test
+
+        <session-dir>/<arch>-<compiler>-<test-file>.<test-class>.<test-method>
+        """
+        dname = os.path.join(os.environ["LLDB_TEST"],
+                     os.environ["LLDB_SESSION_DIRNAME"])
+        if not os.path.isdir(dname):
+            os.mkdir(dname)
+
+        compiler = self.getCompiler()
+
+        if compiler[1] == ':':
+            compiler = compiler[2:]
+
+        fname = "{}-{}-{}".format(self.getArchitecture(), "_".join(compiler.split(os.path.sep)), self.id())
+        if len(fname) > 200:
+            fname = "{}-{}-{}".format(self.getArchitecture(), compiler.split(os.path.sep)[-1], self.id())
+
+        if prefix is not None:
+            fname = "{}-{}".format(prefix, fname)
+
+        return os.path.join(dname, fname)
+
     def dumpSessionInfo(self):
         """
         Dump the debugger interactions leading to a test error/failure.  This
@@ -1363,6 +1393,9 @@ class Base(unittest2.TestCase):
         # formatted tracebacks.
         #
         # See http://docs.python.org/library/unittest.html#unittest.TestResult.
+        src_log_basename = self.getLogBasenameForCurrentTest()
+
+        pairs = []
         if self.__errored__:
             pairs = lldb.test_result.errors
             prefix = 'Error'
@@ -1377,8 +1410,18 @@ class Base(unittest2.TestCase):
         elif self.__unexpected__:
             prefix = "UnexpectedSuccess"
         else:
-            # Simply return, there's no session info to dump!
-            return
+            prefix = "Success"
+            if not lldbtest_config.log_success:
+                # delete log files, return (don't output trace)
+                for i in glob.glob(src_log_basename + "*"):
+                    os.unlink(i)
+                return
+
+        # rename all log files - prepend with result
+        dst_log_basename = self.getLogBasenameForCurrentTest(prefix)
+        for src in glob.glob(self.getLogBasenameForCurrentTest() + "*"):
+            dst = src.replace(src_log_basename, dst_log_basename)
+            os.rename(src, dst)
 
         if not self.__unexpected__ and not self.__skipped__:
             for test, traceback in pairs:
@@ -1391,18 +1434,7 @@ class Base(unittest2.TestCase):
         else:
             benchmarks = False
 
-        dname = os.path.join(os.environ["LLDB_TEST"],
-                             os.environ["LLDB_SESSION_DIRNAME"])
-        if not os.path.isdir(dname):
-            os.mkdir(dname)
-        compiler = self.getCompiler()
-        if compiler[1] == ':':
-            compiler = compiler[2:]
-
-        fname = "%s-%s-%s-%s.log" % (prefix, self.getArchitecture(), "_".join(compiler.split(os.path.sep)), self.id())
-        if len(fname) > 255:
-            fname = "%s-%s-%s-%s.log" % (prefix, self.getArchitecture(), compiler.split(os.path.sep)[-1], self.id())
-        pname = os.path.join(dname, fname)
+        pname = "{}.log".format(dst_log_basename)
         with open(pname, "w") as f:
             import datetime
             print >> f, "Session info generated @", datetime.datetime.now().ctime()
@@ -1835,6 +1867,55 @@ class TestBase(Base):
                         folder = os.path.dirname(folder)
                         continue
 
+    def enableLogChannelsForCurrentTest(self):
+        if len(lldbtest_config.channels) == 0:
+            return
+
+        # if debug channels are specified in lldbtest_config.channels,
+        # create a new set of log files for every test
+        log_basename = self.getLogBasenameForCurrentTest()
+
+        # confirm that the file is writeable
+        host_log_path = "{}-host.log".format(log_basename)
+        open(host_log_path, 'w').close()
+
+        log_enable = "log enable -Tpn -f {} ".format(host_log_path)
+        for channel_with_categories in lldbtest_config.channels:
+            channel_then_categories = channel_with_categories.split(' ', 1)
+            channel = channel_then_categories[0]
+            if len(channel_then_categories) > 1:
+                categories = channel_then_categories[1]
+            else:
+                categories = "default"
+
+            if channel == "gdb-remote":
+                # communicate gdb-remote categories to debugserver
+                os.environ["LLDB_DEBUGSERVER_LOG_FLAGS"] = categories
+
+            self.ci.HandleCommand(log_enable + channel_with_categories, self.res)
+            if not self.res.Succeeded():
+                raise Exception('log enable failed (check LLDB_LOG_OPTION env variable)')
+
+        # Communicate log path name to debugserver & lldb-server
+        server_log_path = "{}-server.log".format(log_basename)
+        open(server_log_path, 'w').close()
+        os.environ["LLDB_DEBUGSERVER_LOG_FILE"] = server_log_path
+
+        # Communicate channels to lldb-server
+        os.environ["LLDB_SERVER_LOG_CHANNELS"] = ":".join(lldbtest_config.channels)
+
+        if len(lldbtest_config.channels) == 0:
+            return
+
+    def disableLogChannelsForCurrentTest(self):
+        # close all log files that we opened
+        for channel_and_categories in lldbtest_config.channels:
+            # channel format - <channel-name> [<category0> [<category1> ...]]
+            channel = channel_and_categories.split(' ', 1)[0]
+            self.ci.HandleCommand("log disable " + channel, self.res)
+            if not self.res.Succeeded():
+                raise Exception('log disable failed (check LLDB_LOG_OPTION env variable)')
+
     def setUp(self):
         #import traceback
         #traceback.print_stack()
@@ -1871,6 +1952,33 @@ class TestBase(Base):
         if not self.dbg:
             raise Exception('Invalid debugger instance')
 
+        # Retrieve the associated command interpreter instance.
+        self.ci = self.dbg.GetCommandInterpreter()
+        if not self.ci:
+            raise Exception('Could not get the command interpreter')
+
+        # And the result object.
+        self.res = lldb.SBCommandReturnObject()
+
+        # Create the debugger instance if necessary.
+        try:
+            self.dbg = lldb.DBG
+        except AttributeError:
+            self.dbg = lldb.SBDebugger.Create()
+
+        if not self.dbg:
+            raise Exception('Invalid debugger instance')
+
+        # Retrieve the associated command interpreter instance.
+        self.ci = self.dbg.GetCommandInterpreter()
+        if not self.ci:
+            raise Exception('Could not get the command interpreter')
+
+        # And the result object.
+        self.res = lldb.SBCommandReturnObject()
+
+        self.enableLogChannelsForCurrentTest()
+
         #
         # Warning: MAJOR HACK AHEAD!
         # If we are running testsuite remotely (by checking lldb.lldbtest_remote_sandbox),

Added: lldb/trunk/test/lldbtest_config.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/lldbtest_config.py?rev=236956&view=auto
==============================================================================
--- lldb/trunk/test/lldbtest_config.py (added)
+++ lldb/trunk/test/lldbtest_config.py Sun May 10 10:22:09 2015
@@ -0,0 +1,17 @@
+"""
+                     The LLVM Compiler Infrastructure
+
+ This file is distributed under the University of Illinois Open Source
+ License. See LICENSE.TXT for details.
+
+Configuration options for lldbtest.py set by dotest.py during initialization
+"""
+
+# array of strings
+# each string has the name of an lldb channel followed by
+# zero or more categories in that channel
+# ex. "gdb-remote packets"
+channels = []
+
+# leave logs/traces even for successful test runs
+log_success = False

Modified: lldb/trunk/tools/lldb-server/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/lldb-server/CMakeLists.txt?rev=236956&r1=236955&r2=236956&view=diff
==============================================================================
--- lldb/trunk/tools/lldb-server/CMakeLists.txt (original)
+++ lldb/trunk/tools/lldb-server/CMakeLists.txt Sun May 10 10:22:09 2015
@@ -2,6 +2,7 @@ set(LLVM_NO_RTTI 1)
 
 if ( CMAKE_SYSTEM_NAME MATCHES "Linux" )
 include_directories(
+  ../../../../llvm/include
   ../../source/Plugins/Process/Linux
   ../../source/Plugins/Process/POSIX
   )
@@ -9,6 +10,7 @@ endif ()
 
 if ( CMAKE_SYSTEM_NAME MATCHES "FreeBSD" )
 include_directories(
+  ../../../../llvm/include
   ../../source/Plugins/Process/FreeBSD
   ../../source/Plugins/Process/POSIX
   )

Modified: lldb/trunk/tools/lldb-server/lldb-gdbserver.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/lldb-server/lldb-gdbserver.cpp?rev=236956&r1=236955&r2=236956&view=diff
==============================================================================
--- lldb/trunk/tools/lldb-server/lldb-gdbserver.cpp (original)
+++ lldb/trunk/tools/lldb-server/lldb-gdbserver.cpp Sun May 10 10:22:09 2015
@@ -23,6 +23,8 @@
 // C++ Includes
 
 // Other libraries and framework includes
+#include "llvm/ADT/StringRef.h"
+
 #include "lldb/Core/Error.h"
 #include "lldb/Core/ConnectionMachPort.h"
 #include "lldb/Core/Debugger.h"
@@ -48,6 +50,7 @@
 #define LLGS_VERSION_STR "local_build"
 #endif
 
+using namespace llvm;
 using namespace lldb;
 using namespace lldb_private;
 using namespace lldb_private::process_gdb_remote;
@@ -73,9 +76,8 @@ static struct option g_long_options[] =
     { "debug",              no_argument,        &g_debug,           1   },
     { "platform",           required_argument,  NULL,               'p' },
     { "verbose",            no_argument,        &g_verbose,         1   },
-    { "lldb-command",       required_argument,  NULL,               'c' },
     { "log-file",           required_argument,  NULL,               'l' },
-    { "log-flags",          required_argument,  NULL,               'f' },
+    { "log-channels",       required_argument,  NULL,               'c' },
     { "attach",             required_argument,  NULL,               'a' },
     { "named-pipe",         required_argument,  NULL,               'N' },
     { "pipe",               required_argument,  NULL,               'U' },
@@ -161,21 +163,6 @@ dump_available_platforms (FILE *output_f
     }
 }
 
-static void
-run_lldb_commands (const lldb::DebuggerSP &debugger_sp, const std::vector<std::string> &lldb_commands)
-{
-    for (const auto &lldb_command : lldb_commands)
-    {
-        printf("(lldb) %s\n", lldb_command.c_str ());
-
-        lldb_private::CommandReturnObject result;
-        debugger_sp->GetCommandInterpreter ().HandleCommand (lldb_command.c_str (), eLazyBoolNo, result);
-        const char *output = result.GetOutputData ();
-        if (output && output[0])
-            puts (output);
-    }
-}
-
 static lldb::PlatformSP
 setup_platform (const std::string &platform_name)
 {
@@ -554,13 +541,14 @@ main_gdbserver (int argc, char *argv[])
     argc--;
     argv++;
     int long_option_index = 0;
-    StreamSP log_stream_sp;
     Args log_args;
     Error error;
     int ch;
     std::string platform_name;
     std::string attach_target;
     std::string named_pipe_path;
+    std::string log_file;
+    StringRef log_channels; // e.g. "lldb process threads:gdb-remote default:linux all"
     int unnamed_pipe_fd = -1;
     bool reverse_connect = false;
 
@@ -595,41 +583,12 @@ main_gdbserver (int argc, char *argv[])
 
         case 'l': // Set Log File
             if (optarg && optarg[0])
-            {
-                if ((strcasecmp(optarg, "stdout") == 0) || (strcmp(optarg, "/dev/stdout") == 0))
-                {
-                    log_stream_sp.reset (new StreamFile (stdout, false));
-                }
-                else if ((strcasecmp(optarg, "stderr") == 0) || (strcmp(optarg, "/dev/stderr") == 0))
-                {
-                    log_stream_sp.reset (new StreamFile (stderr, false));
-                }
-                else
-                {
-                    FILE *log_file = fopen(optarg, "w");
-                    if (log_file)
-                    {
-                        setlinebuf(log_file);
-                        log_stream_sp.reset (new StreamFile (log_file, true));
-                    }
-                    else
-                    {
-                        const char *errno_str = strerror(errno);
-                        fprintf (stderr, "Failed to open log file '%s' for writing: errno = %i (%s)", optarg, errno, errno_str ? errno_str : "unknown error");
-                    }
-
-                }
-            }
-            break;
-
-        case 'f': // Log Flags
-            if (optarg && optarg[0])
-                log_args.AppendArgument(optarg);
+                log_file.assign(optarg);
             break;
 
-        case 'c': // lldb commands
+        case 'c': // Log Channels
             if (optarg && optarg[0])
-                lldb_commands.push_back(optarg);
+                log_channels = StringRef(optarg);
             break;
 
         case 'p': // platform name
@@ -696,12 +655,29 @@ main_gdbserver (int argc, char *argv[])
         exit(option_error);
     }
 
-    if (log_stream_sp)
-    {
-        if (log_args.GetArgumentCount() == 0)
-            log_args.AppendArgument("default");
-        ProcessGDBRemoteLog::EnableLog (log_stream_sp, 0,log_args.GetConstArgumentVector(), log_stream_sp.get());
+    SmallVector<StringRef, 32> channel_array;
+    log_channels.split(channel_array, ":");
+    uint32_t log_options = 0;
+    for (auto channel_with_categories : channel_array)
+    {
+        StreamString error_stream;
+        Args channel_then_categories(channel_with_categories);
+        std::string channel(channel_then_categories.GetArgumentAtIndex(0));
+        channel_then_categories.Shift ();  // Shift off the channel
+        bool success = debugger_sp->EnableLog (channel.c_str(),
+                                               channel_then_categories.GetConstArgumentVector(),
+                                               log_file.c_str(),
+                                               log_options,
+                                               error_stream);
+        if (!success)
+        {
+            fprintf(stderr, "Unable to open log file '%s' for channel \"%s\"",
+                    log_file.c_str(),
+                    channel_with_categories.str().c_str());
+            return -1;
+        }
     }
+
     Log *log(lldb_private::GetLogIfAnyCategoriesSet (GDBR_LOG_VERBOSE));
     if (log)
     {
@@ -722,9 +698,6 @@ main_gdbserver (int argc, char *argv[])
         exit(255);
     }
 
-    // Run any commands requested.
-    run_lldb_commands (debugger_sp, lldb_commands);
-
     // Setup the platform that GDBRemoteCommunicationServerLLGS will use.
     lldb::PlatformSP platform_sp = setup_platform (platform_name);
 





More information about the lldb-commits mailing list