summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp9
-rwxr-xr-xlldb/test/dotest.py40
-rw-r--r--lldb/test/lldbtest.py138
-rw-r--r--lldb/test/lldbtest_config.py17
-rw-r--r--lldb/tools/lldb-server/CMakeLists.txt2
-rw-r--r--lldb/tools/lldb-server/lldb-gdbserver.cpp87
6 files changed, 220 insertions, 73 deletions
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
index a6d9f953b5d..064b60a238a 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
@@ -850,12 +850,21 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *hostname,
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;
diff --git a/lldb/test/dotest.py b/lldb/test/dotest.py
index c6485f53c55..6ac0ad8e5f3 100755
--- a/lldb/test/dotest.py
+++ b/lldb/test/dotest.py
@@ -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 session logs for test failures or errors.
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))
diff --git a/lldb/test/lldbtest.py b/lldb/test/lldbtest.py
index f6f02d9c39b..269b79190dd 100644
--- a/lldb/test/lldbtest.py
+++ b/lldb/test/lldbtest.py
@@ -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),
diff --git a/lldb/test/lldbtest_config.py b/lldb/test/lldbtest_config.py
new file mode 100644
index 00000000000..96703d728de
--- /dev/null
+++ b/lldb/test/lldbtest_config.py
@@ -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
diff --git a/lldb/tools/lldb-server/CMakeLists.txt b/lldb/tools/lldb-server/CMakeLists.txt
index 07552606069..f552b95c7dd 100644
--- a/lldb/tools/lldb-server/CMakeLists.txt
+++ b/lldb/tools/lldb-server/CMakeLists.txt
@@ -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
)
diff --git a/lldb/tools/lldb-server/lldb-gdbserver.cpp b/lldb/tools/lldb-server/lldb-gdbserver.cpp
index a691337f1a9..fbec4386351 100644
--- a/lldb/tools/lldb-server/lldb-gdbserver.cpp
+++ b/lldb/tools/lldb-server/lldb-gdbserver.cpp
@@ -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_file)
}
}
-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");
- }
-
- }
- }
+ log_file.assign(optarg);
break;
- case 'f': // Log Flags
+ case 'c': // Log Channels
if (optarg && optarg[0])
- log_args.AppendArgument(optarg);
- break;
-
- case 'c': // lldb commands
- 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)
+ SmallVector<StringRef, 32> channel_array;
+ log_channels.split(channel_array, ":");
+ uint32_t log_options = 0;
+ for (auto channel_with_categories : channel_array)
{
- if (log_args.GetArgumentCount() == 0)
- log_args.AppendArgument("default");
- ProcessGDBRemoteLog::EnableLog (log_stream_sp, 0,log_args.GetConstArgumentVector(), log_stream_sp.get());
+ 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);
OpenPOWER on IntegriCloud