summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
Diffstat (limited to 'cli')
-rw-r--r--cli/CMakeLists.txt27
-rw-r--r--cli/cli.hpp6
-rw-r--r--cli/cli_diff.cpp10
-rw-r--r--cli/cli_diff_images.cpp10
-rw-r--r--cli/cli_diff_state.cpp71
-rw-r--r--cli/cli_leaks.cpp85
-rw-r--r--cli/cli_main.cpp2
-rw-r--r--cli/cli_pager.cpp47
-rw-r--r--cli/cli_pager.hpp4
-rw-r--r--cli/cli_pickle.cpp30
-rw-r--r--cli/cli_repack.cpp30
-rw-r--r--cli/cli_resources.cpp7
-rw-r--r--cli/cli_resources.hpp4
-rw-r--r--cli/cli_retrace.hpp4
-rw-r--r--cli/cli_sed.cpp59
-rw-r--r--cli/cli_trace.cpp99
-rw-r--r--cli/cli_trim.cpp284
-rw-r--r--cli/cli_trim_auto.cpp432
-rw-r--r--cli/cli_trim_auto_analyzer.cpp (renamed from cli/trace_analyzer.cpp)2
-rw-r--r--cli/cli_trim_auto_analyzer.hpp (renamed from cli/trace_analyzer.hpp)0
-rw-r--r--cli/pickle.hpp62
21 files changed, 841 insertions, 434 deletions
diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt
index 727cd7f8..31d7fdf2 100644
--- a/cli/CMakeLists.txt
+++ b/cli/CMakeLists.txt
@@ -8,12 +8,18 @@ add_definitions(
-DAPITRACE_SCRIPTS_INSTALL_DIR="${CMAKE_INSTALL_PREFIX}/${SCRIPTS_INSTALL_DIR}"
-DAPITRACE_WRAPPERS_INSTALL_DIR="${CMAKE_INSTALL_PREFIX}/${WRAPPER_INSTALL_DIR}"
)
+if (WIN32)
+ add_definitions (-DAPITRACE_PYTHON_EXECUTABLE="python")
+else ()
+ add_definitions (-DAPITRACE_PYTHON_EXECUTABLE="${PYTHON_EXECUTABLE}")
+endif ()
add_executable (apitrace
cli_main.cpp
cli_diff.cpp
cli_diff_state.cpp
cli_diff_images.cpp
+ cli_leaks.cpp
cli_dump.cpp
cli_dump_images.cpp
cli_pager.cpp
@@ -23,8 +29,9 @@ add_executable (apitrace
cli_sed.cpp
cli_trace.cpp
cli_trim.cpp
+ cli_trim_auto.cpp
+ cli_trim_auto_analyzer.cpp
cli_resources.cpp
- trace_analyzer.cpp
)
target_link_libraries (apitrace
@@ -36,22 +43,12 @@ target_link_libraries (apitrace
)
if (NOT CMAKE_CROSSCOMPILING)
+ # On debug builds tell where the source is so that scripts can be found
+ # http://www.cmake.org/cmake/help/v3.0/policy/CMP0043.html
set_target_properties (apitrace PROPERTIES
- # On debug builds tell where the source is so that scripts can be found
- COMPILE_DEFINITIONS_DEBUG APITRACE_SOURCE_DIR="${CMAKE_SOURCE_DIR}"
- )
-endif ()
-
-if (MSVC AND NOT CMAKE_GENERATOR STREQUAL "Ninja")
- # On MSVC builds tell which subdirectory the binaries with be (for each
- # configuration)
- set_target_properties (apitrace PROPERTIES
- COMPILE_DEFINITIONS_DEBUG APITRACE_CONFIGURATION_SUBDIR="Debug"
- COMPILE_DEFINITIONS_RELEASE APITRACE_CONFIGURATION_SUBDIR="Release"
- COMPILE_DEFINITIONS_MINSIZEREL APITRACE_CONFIGURATION_SUBDIR="MinSizeRel"
- COMPILE_DEFINITIONS_RELWITHDEBINFO APITRACE_CONFIGURATION_SUBDIR="RelWithDebInfo"
+ COMPILE_DEFINITIONS $<$<CONFIG:Debug>:APITRACE_SOURCE_DIR="${CMAKE_SOURCE_DIR}">
)
endif ()
-
install (TARGETS apitrace RUNTIME DESTINATION bin)
+install_pdb (apitrace RUNTIME DESTINATION bin)
diff --git a/cli/cli.hpp b/cli/cli.hpp
index 088f0e8c..f777122c 100644
--- a/cli/cli.hpp
+++ b/cli/cli.hpp
@@ -25,8 +25,7 @@
*
*********************************************************************/
-#ifndef _APITRACE_CLI_HPP_
-#define _APITRACE_CLI_HPP_
+#pragma once
struct Command {
@@ -45,11 +44,12 @@ extern const Command diff_state_command;
extern const Command diff_images_command;
extern const Command dump_command;
extern const Command dump_images_command;
+extern const Command leaks_command;
extern const Command pickle_command;
extern const Command repack_command;
extern const Command retrace_command;
extern const Command sed_command;
extern const Command trace_command;
extern const Command trim_command;
+extern const Command trim_auto_command;
-#endif /* _APITRACE_CLI_HPP_ */
diff --git a/cli/cli_diff.cpp b/cli/cli_diff.cpp
index 76cdce1d..daeccd3e 100644
--- a/cli/cli_diff.cpp
+++ b/cli/cli_diff.cpp
@@ -45,12 +45,9 @@ static void
usage(void)
{
os::String command = find_command();
- if (!command.length()) {
- exit(1);
- }
char *args[4];
- args[0] = (char *) "python";
+ args[0] = (char *) APITRACE_PYTHON_EXECUTABLE;
args[1] = (char *) command.str();
args[2] = (char *) "--help";
args[3] = NULL;
@@ -64,14 +61,11 @@ command(int argc, char *argv[])
int i;
os::String command = find_command();
- if (!command.length()) {
- return 1;
- }
os::String apitracePath = os::getProcessName();
std::vector<const char *> args;
- args.push_back("python");
+ args.push_back(APITRACE_PYTHON_EXECUTABLE);
args.push_back(command.str());
args.push_back("--apitrace");
args.push_back(apitracePath.str());
diff --git a/cli/cli_diff_images.cpp b/cli/cli_diff_images.cpp
index ba8df3e9..54523cd3 100644
--- a/cli/cli_diff_images.cpp
+++ b/cli/cli_diff_images.cpp
@@ -45,12 +45,9 @@ static void
usage(void)
{
os::String command = find_command();
- if (!command.length()) {
- exit(1);
- }
char *args[4];
- args[0] = (char *) "python";
+ args[0] = (char *) APITRACE_PYTHON_EXECUTABLE;
args[1] = (char *) command.str();
args[2] = (char *) "--help";
args[3] = NULL;
@@ -64,12 +61,9 @@ command(int argc, char *argv[])
int i;
os::String command = find_command();
- if (!command.length()) {
- return 1;
- }
std::vector<const char *> args;
- args.push_back("python");
+ args.push_back(APITRACE_PYTHON_EXECUTABLE);
args.push_back(command.str());
for (i = 1; i < argc; i++) {
args.push_back(argv[i]);
diff --git a/cli/cli_diff_state.cpp b/cli/cli_diff_state.cpp
index a802b12a..0488e77f 100644
--- a/cli/cli_diff_state.cpp
+++ b/cli/cli_diff_state.cpp
@@ -26,8 +26,6 @@
*********************************************************************/
#include <string.h>
-#include <getopt.h>
-
#include <iostream>
#include "cli.hpp"
@@ -37,63 +35,42 @@
static const char *synopsis = "Identify differences between two state dumps.";
+static os::String
+find_command(void)
+{
+ return findScript("jsondiff.py");
+}
+
static void
usage(void)
{
- std::cout
- << "usage: apitrace diff-state <state-1> <state-2>\n"
- << synopsis << "\n"
- "\n"
- " Both input files should be the result of running 'glretrace -D XYZ <trace>'.\n";
-}
+ os::String command = find_command();
-const static char *
-shortOptions = "h";
+ char *args[4];
+ args[0] = (char *) APITRACE_PYTHON_EXECUTABLE;
+ args[1] = (char *) command.str();
+ args[2] = (char *) "--help";
+ args[3] = NULL;
-const static struct option
-longOptions[] = {
- {"help", no_argument, 0, 'h'},
- {0, 0, 0, 0}
-};
+ os::execute(args);
+}
static int
command(int argc, char *argv[])
{
- int opt;
- while ((opt = getopt_long(argc, argv, shortOptions, longOptions, NULL)) != -1) {
- switch (opt) {
- case 'h':
- usage();
- return 0;
- default:
- std::cerr << "error: unexpected option `" << (char)opt << "`\n";
- usage();
- return 1;
- }
- }
-
- if (argc != optind + 2) {
- std::cerr << "Error: diff-state requires exactly two state-dump files as arguments.\n";
- usage();
- return 1;
- }
-
- char *file1, *file2;
+ int i;
- file1 = argv[optind];
- file2 = argv[optind + 1];
+ os::String command = find_command();
- os::String command = findScript("jsondiff.py");
-
- char *args[5];
-
- args[0] = const_cast<char *>("python");
- args[1] = const_cast<char *>(command.str());
- args[2] = file1;
- args[3] = file2;
- args[4] = NULL;
+ std::vector<const char *> args;
+ args.push_back(APITRACE_PYTHON_EXECUTABLE);
+ args.push_back(command.str());
+ for (i = 1; i < argc; i++) {
+ args.push_back(argv[i]);
+ }
+ args.push_back(NULL);
- return os::execute(args);
+ return os::execute((char * const *)&args[0]);
}
const Command diff_state_command = {
diff --git a/cli/cli_leaks.cpp b/cli/cli_leaks.cpp
new file mode 100644
index 00000000..1d28ec58
--- /dev/null
+++ b/cli/cli_leaks.cpp
@@ -0,0 +1,85 @@
+/*********************************************************************
+ *
+ * Copyright 2016 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ *********************************************************************/
+
+#include <string.h>
+#include <iostream>
+
+#include "cli.hpp"
+#include "os_string.hpp"
+#include "os_process.hpp"
+#include "cli_resources.hpp"
+
+static const char *synopsis = "Check trace for object leaks.";
+
+static os::String
+find_command(void)
+{
+ return findScript("leaks.py");
+}
+
+static void
+usage(void)
+{
+ os::String command = find_command();
+
+ char *args[4];
+ args[0] = (char *) APITRACE_PYTHON_EXECUTABLE;
+ args[1] = (char *) command.str();
+ args[2] = (char *) "--help";
+ args[3] = NULL;
+
+ os::execute(args);
+}
+
+static int
+command(int argc, char *argv[])
+{
+ int i;
+
+ os::String command = find_command();
+
+ os::String apitracePath = os::getProcessName();
+
+ std::vector<const char *> args;
+ args.push_back("python");
+ args.push_back(command.str());
+ args.push_back("--apitrace");
+ args.push_back(apitracePath.str());
+ for (i = 1; i < argc; i++) {
+ args.push_back(argv[i]);
+ }
+ args.push_back(NULL);
+
+ return os::execute((char * const *)&args[0]);
+}
+
+const Command leaks_command = {
+ "leaks",
+ synopsis,
+ usage,
+ command
+};
diff --git a/cli/cli_main.cpp b/cli/cli_main.cpp
index 1e278e41..b475bd3b 100644
--- a/cli/cli_main.cpp
+++ b/cli/cli_main.cpp
@@ -71,12 +71,14 @@ static const Command * commands[] = {
&diff_images_command,
&dump_command,
&dump_images_command,
+ &leaks_command,
&pickle_command,
&sed_command,
&repack_command,
&retrace_command,
&trace_command,
&trim_command,
+ &trim_auto_command,
&help_command
};
diff --git a/cli/cli_pager.cpp b/cli/cli_pager.cpp
index 579b19f3..4a3b451b 100644
--- a/cli/cli_pager.cpp
+++ b/cli/cli_pager.cpp
@@ -64,8 +64,8 @@ static pid_t pid = -1;
static void
on_exit(void)
{
- fflush(stdout);
- fflush(stderr);
+ fflush(stdout);
+ fflush(stderr);
close(STDOUT_FILENO);
close(STDERR_FILENO);
waitpid(pid, NULL, 0);
@@ -78,6 +78,7 @@ on_exit(void)
static void
on_signal(int sig)
{
+ fprintf(stderr, "on_signal\n");
on_exit();
signal(sig, SIG_DFL);
raise(sig);
@@ -93,37 +94,45 @@ pipepager(void) {
return;
}
- enum {
- READ_FD = 0,
- WRITE_FD = 1
- };
+ union {
+ int pipe[2];
+ struct {
+ int read;
+ int write;
+ };
+ } fd;
- int parentToChild[2];
int ret;
const char *pager;
- ret = pipe(parentToChild);
+ ret = pipe(fd.pipe);
assert(ret == 0);
+ if (ret != 0) {
+ return;
+ }
pid = fork();
switch (pid) {
case -1:
// failed to fork
+ close(fd.read);
+ close(fd.write);
return;
case 0:
// child
- ret = dup2(parentToChild[READ_FD], STDIN_FILENO);
- assert(ret != -1);
- ret = close(parentToChild[WRITE_FD]);
- assert(ret == 0);
+ close(fd.write);
+
+ dup2(fd.read, STDIN_FILENO);
pager = getenv("PAGER");
if (!pager) {
pager = "less";
}
- setenv("LESS", "FRXn", 0);
+ if (!getenv("PAGER")) {
+ putenv((char *)"LESS=FRXn");
+ }
execlp(pager, pager, NULL);
@@ -132,13 +141,13 @@ pipepager(void) {
default:
// parent
- ret = close(parentToChild[READ_FD]);
- assert(ret == 0);
+ close(fd.read);
- dup2(parentToChild[WRITE_FD], STDOUT_FILENO);
- if (isatty(STDERR_FILENO))
- dup2(parentToChild[WRITE_FD], STDERR_FILENO);
- close(parentToChild[WRITE_FD]);
+ dup2(fd.write, STDOUT_FILENO);
+ if (isatty(STDERR_FILENO)) {
+ dup2(fd.write, STDERR_FILENO);
+ }
+ close(fd.write);
// Ensure we wait for the pager before terminating
signal(SIGINT, on_signal);
diff --git a/cli/cli_pager.hpp b/cli/cli_pager.hpp
index 95d98781..197d712f 100644
--- a/cli/cli_pager.hpp
+++ b/cli/cli_pager.hpp
@@ -24,12 +24,10 @@
**************************************************************************/
-#ifndef _CLI_PAGER_HPP_
-#define _CLI_PAGER_HPP_
+#pragma once
void
pipepager(void);
-#endif /* _CLI_PAGER_HPP_ */
diff --git a/cli/cli_pickle.cpp b/cli/cli_pickle.cpp
index 5c267e0e..286716be 100644
--- a/cli/cli_pickle.cpp
+++ b/cli/cli_pickle.cpp
@@ -83,6 +83,10 @@ public:
writer.writeString(node->value);
}
+ void visit(WString *node) {
+ writer.writeWString(node->value);
+ }
+
void visit(Enum *node) {
if (symbolic) {
const EnumValue *it = node->lookup();
@@ -98,7 +102,7 @@ public:
if (symbolic) {
unsigned long long value = node->value;
const BitmaskSig *sig = node->sig;
- writer.beginList();
+ writer.beginTuple();
for (const BitmaskFlag *it = sig->flags; it != sig->flags + sig->num_flags; ++it) {
if ((it->value && (value & it->value) == it->value) ||
(!it->value && value == 0)) {
@@ -112,14 +116,15 @@ public:
if (value) {
writer.writeInt(value);
}
- writer.endList();
+ writer.endTuple();
} else {
writer.writeInt(node->value);
}
}
void visit(Struct *node) {
- if (false) {
+ if (true) {
+ // Structures as dictionaries
writer.beginDict();
for (unsigned i = 0; i < node->sig->num_members; ++i) {
writer.beginItem(node->sig->member_names[i]);
@@ -128,11 +133,13 @@ public:
}
writer.endDict();
} else {
- writer.beginTuple();
- for (unsigned i = 0; i < node->sig->num_members; ++i) {
+ // Structures as tuples
+ unsigned num_members = node->sig->num_members;
+ writer.beginTuple(num_members);
+ for (unsigned i = 0; i < num_members; ++i) {
_visit(node->members[i]);
}
- writer.endTuple();
+ writer.endTuple(num_members);
}
}
@@ -149,7 +156,7 @@ public:
}
void visit(Pointer *node) {
- writer.writeInt(node->value);
+ writer.writePointer(node->value);
}
void visit(Repr *r) {
@@ -169,11 +176,18 @@ public:
writer.beginList();
for (unsigned i = 0; i < call->args.size(); ++i) {
+ writer.beginTuple(2);
+ if (i < call->sig->num_args) {
+ writer.writeString(call->sig->arg_names[i]);
+ } else {
+ writer.writeNone();
+ }
if (call->args[i].value) {
_visit(call->args[i].value);
} else {
writer.writeNone();
}
+ writer.endTuple(2);
}
writer.endList();
@@ -183,6 +197,8 @@ public:
writer.writeNone();
}
+ writer.writeInt(call->flags);
+
writer.endTuple();
}
};
diff --git a/cli/cli_repack.cpp b/cli/cli_repack.cpp
index 5d122fa6..291f0d96 100644
--- a/cli/cli_repack.cpp
+++ b/cli/cli_repack.cpp
@@ -32,40 +32,54 @@
#include "cli.hpp"
#include "trace_file.hpp"
+#include "trace_ostream.hpp"
-static const char *synopsis = "Repack a trace file with Snappy compression.";
+static const char *synopsis = "Repack a trace file with different compression.";
static void
usage(void)
{
std::cout
- << "usage: apitrace repack <in-trace-file> <out-trace-file>\n"
+ << "usage: apitrace repack [options] <in-trace-file> <out-trace-file>\n"
<< synopsis << "\n"
<< "\n"
<< "Snappy compression allows for faster replay and smaller memory footprint,\n"
<< "at the expense of a slightly smaller compression ratio than zlib\n"
+ << "\n"
+ << " -z,--zlib Use ZLib compression instead\n"
<< "\n";
}
const static char *
-shortOptions = "h";
+shortOptions = "hz";
const static struct option
longOptions[] = {
{"help", no_argument, 0, 'h'},
+ {"zlib", no_argument, 0, 'z'},
{0, 0, 0, 0}
};
+enum Format {
+ FORMAT_SNAPPY = 0,
+ FORMAT_ZLIB,
+};
+
static int
-repack(const char *inFileName, const char *outFileName)
+repack(const char *inFileName, const char *outFileName, Format format)
{
trace::File *inFile = trace::File::createForRead(inFileName);
if (!inFile) {
return 1;
}
- trace::File *outFile = trace::File::createForWrite(outFileName);
+ trace::OutStream *outFile;
+ if (format == FORMAT_SNAPPY) {
+ outFile = trace::createSnappyStream(outFileName);
+ } else {
+ outFile = trace::createZLibStream(outFileName);
+ }
if (!outFile) {
delete inFile;
return 1;
@@ -89,12 +103,16 @@ repack(const char *inFileName, const char *outFileName)
static int
command(int argc, char *argv[])
{
+ Format format = FORMAT_SNAPPY;
int opt;
while ((opt = getopt_long(argc, argv, shortOptions, longOptions, NULL)) != -1) {
switch (opt) {
case 'h':
usage();
return 0;
+ case 'z':
+ format = FORMAT_ZLIB;
+ break;
default:
std::cerr << "error: unexpected option `" << (char)opt << "`\n";
usage();
@@ -108,7 +126,7 @@ command(int argc, char *argv[])
return 1;
}
- return repack(argv[optind], argv[optind + 1]);
+ return repack(argv[optind], argv[optind + 1], format);
}
const Command repack_command = {
diff --git a/cli/cli_resources.cpp b/cli/cli_resources.cpp
index d6d0a55d..4186bd60 100644
--- a/cli/cli_resources.cpp
+++ b/cli/cli_resources.cpp
@@ -101,11 +101,11 @@ findWrapper(const char *wrapperFilename, bool verbose)
// Try relative build directory
// XXX: Just make build and install directory layout match
wrapperPath = processDir;
-#if defined(APITRACE_CONFIGURATION_SUBDIR)
+#if defined(CMAKE_INTDIR)
// Go from `Debug\apitrace.exe` to `wrappers\Debug\foo.dll` on MSVC builds.
wrapperPath.join("..");
wrapperPath.join("wrappers");
- wrapperPath.join(APITRACE_CONFIGURATION_SUBDIR);
+ wrapperPath.join(CMAKE_INTDIR);
#else
wrapperPath.join("wrappers");
#endif
@@ -194,6 +194,5 @@ findScript(const char *scriptFilename, bool verbose)
#endif
std::cerr << "error: cannot find " << scriptFilename << " script\n";
-
- return "";
+ exit(1);
}
diff --git a/cli/cli_resources.hpp b/cli/cli_resources.hpp
index ee3882be..27b883c3 100644
--- a/cli/cli_resources.hpp
+++ b/cli/cli_resources.hpp
@@ -23,8 +23,7 @@
*
**************************************************************************/
-#ifndef _CLI_RESOURCES_HPP_
-#define _CLI_RESOURCES_HPP_
+#pragma once
#include <stdlib.h>
@@ -43,4 +42,3 @@ os::String
findWrapper(const char *wrapperFilename, bool verbose = false);
-#endif /* _CLI_RESOURCES_HPP_ */
diff --git a/cli/cli_retrace.hpp b/cli/cli_retrace.hpp
index 0dfa823e..6df0fbe6 100644
--- a/cli/cli_retrace.hpp
+++ b/cli/cli_retrace.hpp
@@ -25,8 +25,7 @@
*
*********************************************************************/
-#ifndef _CLI_RETRACE_HPP_
-#define _CLI_RETRACE_HPP_
+#pragma once
#include <vector>
@@ -43,4 +42,3 @@ executeRetrace(const std::vector<const char *> & opts,
const char *traceName);
-#endif /* _CLI_RETRACE_HPP_ */
diff --git a/cli/cli_sed.cpp b/cli/cli_sed.cpp
index afec1257..401b7a63 100644
--- a/cli/cli_sed.cpp
+++ b/cli/cli_sed.cpp
@@ -46,8 +46,11 @@ usage(void)
<< synopsis << "\n"
"\n"
" -h, --help Show detailed help for sed options and exit\n"
- " -e s/SEARCH/REPLACE/ Search and replace a symbol.\n"
- " XXX: Only works for enums.\n"
+ " -e s/SEARCH/REPLACE/ Search and replace a symbol. Use @file(<path>)\n"
+ " to read SEARCH or REPLACE from a file.\n"
+ " Any character (not just /) can be used as \n"
+ " separator.\n"
+ " XXX: Only works for enums and strings.\n"
" -o, --output=TRACE_FILE Output trace file\n"
;
}
@@ -104,6 +107,16 @@ public:
}
void visit(String *node) {
+ if (!searchName.compare(node->value)) {
+ size_t len = replaceName.length() + 1;
+ delete [] node->value;
+ char *str = new char [len];
+ memcpy(str, replaceName.c_str(), len);
+ node->value = str;
+ }
+ }
+
+ void visit(WString *node) {
}
void visit(Enum *node) {
@@ -212,17 +225,17 @@ sed_trace(Replacements &replacements, const char *inFileName, std::string &outFi
static bool
parseSubstOpt(Replacements &replacements, const char *opt)
{
+ char separator;
+
if (*opt++ != 's') {
return false;
}
- if (*opt++ != '/') {
- return false;
- }
+ separator = *opt++;
// Parse the search pattern
const char *search_begin = opt;
- while (*opt != '/') {
+ while (*opt != separator) {
if (*opt == 0) {
return false;
}
@@ -232,7 +245,7 @@ parseSubstOpt(Replacements &replacements, const char *opt)
// Parse the replace pattern
const char *replace_begin = opt;
- while (*opt != '/') {
+ while (*opt != separator) {
if (*opt == 0) {
return false;
}
@@ -247,6 +260,36 @@ parseSubstOpt(Replacements &replacements, const char *opt)
std::string search(search_begin, search_end);
std::string replace(replace_begin, replace_end);
+ // If search or replace strings are taken from a file, read the file
+ std::string file_subst = "@file(";
+
+ for (int i = 0; i < 2; i++) {
+ std::string *str = i ? &search : &replace;
+
+ if (!str->compare(0, file_subst.length(), file_subst)) {
+ if ((*str)[str->length()-1] != ')') {
+ return false;
+ }
+
+ std::string fname = str->substr(file_subst.length());
+ fname[fname.length()-1] = 0;
+ FILE *f = fopen(fname.c_str(), "rt");
+ if (!f) {
+ std::cerr << "error: cannot open file " << fname << "\n";
+ return false;
+ }
+ char buf[1024];
+ (*str) = "";
+ while (!feof(f)) {
+ if (fgets(buf, 1024, f)) {
+ str->append(buf);
+ }
+ }
+ fclose(f);
+ }
+ }
+
+
replacements.push_back(Replacer(search, replace));
return true;
@@ -270,7 +313,7 @@ command(int argc, char *argv[])
break;
case 'e':
if (!parseSubstOpt(replacements, optarg)) {
- std::cerr << "error: invalid replacement patter `" << optarg << "`\n";
+ std::cerr << "error: invalid replacement pattern `" << optarg << "`\n";
}
break;
default:
diff --git a/cli/cli_trace.cpp b/cli/cli_trace.cpp
index 54c20df2..adca8af3 100644
--- a/cli/cli_trace.cpp
+++ b/cli/cli_trace.cpp
@@ -32,9 +32,11 @@
#include <getopt.h>
#include <iostream>
+#include <fstream>
#include "os_string.hpp"
#include "os_process.hpp"
+#include "os_version.hpp"
#include "cli.hpp"
#include "cli_resources.hpp"
@@ -52,6 +54,8 @@
#endif
+#ifdef _WIN32
+
static inline bool
copyWrapper(const os::String & wrapperPath,
const char *programPath,
@@ -81,12 +85,14 @@ copyWrapper(const os::String & wrapperPath,
return true;
}
+#endif /* _WIN32 */
+
static int
traceProgram(trace::API api,
char * const *argv,
const char *output,
- bool verbose,
+ int verbose,
bool debug)
{
const char *wrapperFilename;
@@ -121,6 +127,10 @@ traceProgram(trace::API api,
wrapperFilename = "dxgitrace.dll";
useInject = true;
break;
+ case trace::API_D2D1:
+ wrapperFilename = "d2d1trace.dll";
+ useInject = true;
+ break;
#endif
default:
std::cerr << "error: unsupported API\n";
@@ -136,27 +146,21 @@ traceProgram(trace::API api,
#if defined(_WIN32)
/*
* Use DLL injection method on Windows, even for APIs that don't stricly
- * need it. Except when tracing OpenGL on Windows 8, as the injection
- * method seems to have troubles tracing the internal
- * gdi32.dll!SwapBuffers -> opengl32.dll!wglSwapBuffer calls, per github
- * issue #172.
+ * need it.
*/
- {
- OSVERSIONINFO osvi;
- ZeroMemory(&osvi, sizeof osvi);
- osvi.dwOSVersionInfoSize = sizeof osvi;
- GetVersionEx(&osvi);
- BOOL bIsWindows8orLater =
- osvi.dwMajorVersion > 6 ||
- (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion >= 2);
- if (api != trace::API_GL || !bIsWindows8orLater) {
- useInject = true;
- }
- }
+ useInject = true;
if (useInject) {
args.push_back("inject");
+ if (debug) {
+ args.push_back("-d");
+ }
+ for (int i = 1; i < verbose; ++i) {
+ args.push_back("-v");
+ }
+ args.push_back("-D");
args.push_back(wrapperPath);
+ args.push_back("--");
} else {
/* On Windows copy the wrapper to the program directory.
*/
@@ -187,14 +191,50 @@ traceProgram(trace::API api,
wrapperPath.append(oldEnvVarValue);
}
+ std::string ex;
if (debug) {
- std::string ex("set exec-wrapper env " TRACE_VARIABLE "=");
- ex.append(wrapperPath.str());
+#if defined(__APPLE__)
+ bool lldb = true;
+#else
+ bool lldb = false;
+#endif
- args.push_back("gdb");
- args.push_back("--ex");
- args.push_back(ex.c_str());
- args.push_back("--args");
+ if (lldb) {
+ /*
+ * Debug with LLDB.
+ *
+ * See also http://lldb.llvm.org/lldb-gdb.html
+ */
+
+ char scriptFileName[] = "/tmp/apitrace.XXXXXX";
+ int scriptFD = mkstemp(scriptFileName);
+ if (scriptFD < 0) {
+ std::cerr << "error: failed to create temporary lldb script file\n";
+ exit(1);
+ }
+
+ FILE *scriptStream = fdopen(scriptFD, "w");
+ fprintf(scriptStream, "env " TRACE_VARIABLE "='%s'\n", wrapperPath.str());
+ fclose(scriptStream);
+
+ args.push_back("lldb");
+ args.push_back("-s");
+ args.push_back(scriptFileName);
+ args.push_back("--");
+ } else {
+ /*
+ * Debug with GDB.
+ */
+
+ ex = "set exec-wrapper env " TRACE_VARIABLE "='";
+ ex.append(wrapperPath.str());
+ ex.append("'");
+
+ args.push_back("gdb");
+ args.push_back("--ex");
+ args.push_back(ex.c_str());
+ args.push_back("--args");
+ }
os::unsetEnvironment(TRACE_VARIABLE);
} else {
@@ -287,7 +327,7 @@ usage(void)
" -o, --output=TRACE specify output trace file;\n"
" default is `PROGRAM.trace`\n"
#ifdef TRACE_VARIABLE
- " -d, --debug debug with gdb\n"
+ " -d, --debug run inside debugger (gdb/lldb)\n"
#endif
;
}
@@ -308,7 +348,7 @@ longOptions[] = {
static int
command(int argc, char *argv[])
{
- bool verbose = false;
+ int verbose = 0;
trace::API api = trace::API_GL;
const char *output = NULL;
bool debug = false;
@@ -320,14 +360,16 @@ command(int argc, char *argv[])
usage();
return 0;
case 'v':
- verbose = true;
+ ++verbose;
break;
case 'a':
if (strcmp(optarg, "gl") == 0) {
api = trace::API_GL;
} else if (strcmp(optarg, "egl") == 0) {
api = trace::API_EGL;
- } else if (strcmp(optarg, "d3d7") == 0) {
+ } else if (strcmp(optarg, "ddraw") == 0 ||
+ strcmp(optarg, "d3d6") == 0 ||
+ strcmp(optarg, "d3d7") == 0) {
api = trace::API_D3D7;
} else if (strcmp(optarg, "d3d8") == 0) {
api = trace::API_D3D8;
@@ -339,6 +381,9 @@ command(int argc, char *argv[])
strcmp(optarg, "d3d11") == 0 ||
strcmp(optarg, "d3d11_1") == 0) {
api = trace::API_DXGI;
+ } else if (strcmp(optarg, "d2d") == 0 ||
+ strcmp(optarg, "d2d1") == 0) {
+ api = trace::API_D2D1;
} else {
std::cerr << "error: unknown API `" << optarg << "`\n";
usage();
diff --git a/cli/cli_trim.cpp b/cli/cli_trim.cpp
index ed950e5c..b1e6b485 100644
--- a/cli/cli_trim.cpp
+++ b/cli/cli_trim.cpp
@@ -29,13 +29,10 @@
#include <limits.h> // for CHAR_MAX
#include <getopt.h>
-#include <set>
-
#include "cli.hpp"
#include "os_string.hpp"
-#include "trace_analyzer.hpp"
#include "trace_callset.hpp"
#include "trace_parser.hpp"
#include "trace_writer.hpp"
@@ -52,98 +49,15 @@ usage(void)
" -h, --help Show detailed help for trim options and exit\n"
" --calls=CALLSET Include specified calls in the trimmed output.\n"
" --frames=FRAMESET Include specified frames in the trimmed output.\n"
- " --deps Include additional calls to satisfy dependencies\n"
- " --no-deps Do not include any more calls than requestd\n"
- " --prune Omit calls without side effects from the output\n"
- " --no-prune Do not omit any requested calls\n"
- " -a, --auto Trim automatically to calls specified in --calls/--frames\n"
- " Equivalent to both --deps and --prune\n"
- " --exact Trim to exactly the calls specified in --calls/--frames\n"
- " Equivalent to both --no-deps and --no-prune\n"
- " --print-callset Print the final set of calls included in output\n"
- " --trim-spec=SPEC Only performing trimming as described in SPEC\n"
- " --thread=THREAD_ID Only retain calls from specified thread\n"
- " -o, --output=TRACE_FILE Output trace file\n"
- ;
-}
-
-static void
-help()
-{
- std::cout
- << "usage: apitrace trim [OPTIONS] TRACE_FILE...\n"
- << synopsis << "\n"
- "\n"
- " -h, --help Show this help message and exit\n"
- "\n"
- " --calls=CALLSET Include specified calls in the trimmed output.\n"
- " --frames=FRAMESET Include specified frames in the trimmed output.\n"
- "\n"
- " --deps Perform dependency analysis and include dependent\n"
- " calls as needed, (even if those calls were not\n"
- " explicitly requested with --calls or --frames).\n"
- " (On by default. See --no-deps or --exact)\n"
- "\n"
- " --no-deps Do not perform dependency analysis. Output will\n"
- " not include any additional calls beyond those\n"
- " explicitly requested with --calls or --frames).\n"
- "\n"
- " --prune Omit calls with no side effects, even if the call\n"
- " is within the range specified by --calls/--frames.\n"
- " (On by default. See --no-prune or --exact)\n"
- "\n"
- " --no-prune Never omit any calls from the range specified\n"
- " --calls/--frames.\n"
- "\n"
- " -a, --auto Use dependency analysis and pruning\n"
- " of uninteresting calls the resulting trace may\n"
- " include more and less calls than specified.\n"
- " This option is equivalent\n"
- " to passing both --deps and --prune and is on by\n"
- " default (see --no-deps, --no-prune and --exact)\n"
- "\n"
- " --exact Trim output to exact the calls or frames\n"
- " specified with --calls or --frames.\n"
- " This option is equivalent\n"
- " to passing both --no-deps and --no-prune.\n"
- "\n"
- " --print-callset Print to stdout the final set of calls included\n"
- " in the trim output. This can be useful for\n"
- " tweaking the trimmed callset from --auto on the\n"
- " command-line.\n"
- " Use --calls=@FILE to read callset from a file.\n"
- "\n"
- " --trim-spec=SPEC Specifies which classes of calls will be trimmed.\n"
- " This option only has an effect if dependency\n"
- " analysis is enabled. The argument is a comma-\n"
- " separated list of names from the following:\n"
- "\n"
- " no-side-effects Calls with no side effects\n"
- " textures Calls to setup unused textures\n"
- " shaders Calls to setup unused shaders\n"
- " drawing Calls that draw\n"
- "\n"
- " The default trim specification includes all of\n"
- " the above, (as much as possible will be trimmed).\n"
- "\n"
" --thread=THREAD_ID Only retain calls from specified thread\n"
- "\n"
" -o, --output=TRACE_FILE Output trace file\n"
- "\n"
;
}
enum {
CALLS_OPT = CHAR_MAX + 1,
FRAMES_OPT,
- DEPS_OPT,
- NO_DEPS_OPT,
- PRUNE_OPT,
- NO_PRUNE_OPT,
- THREAD_OPT,
- PRINT_CALLSET_OPT,
- TRIM_SPEC_OPT,
- EXACT_OPT
+ THREAD_OPT
};
const static char *
@@ -154,16 +68,8 @@ longOptions[] = {
{"help", no_argument, 0, 'h'},
{"calls", required_argument, 0, CALLS_OPT},
{"frames", required_argument, 0, FRAMES_OPT},
- {"deps", no_argument, 0, DEPS_OPT},
- {"no-deps", no_argument, 0, NO_DEPS_OPT},
- {"prune", no_argument, 0, PRUNE_OPT},
- {"no-prune", no_argument, 0, NO_PRUNE_OPT},
- {"auto", no_argument, 0, 'a'},
- {"exact", no_argument, 0, EXACT_OPT},
{"thread", required_argument, 0, THREAD_OPT},
{"output", required_argument, 0, 'o'},
- {"print-callset", no_argument, 0, PRINT_CALLSET_OPT},
- {"trim-spec", required_argument, 0, TRIM_SPEC_OPT},
{0, 0, 0, 0}
};
@@ -180,91 +86,24 @@ struct trim_options {
/* Frames to be included in trace. */
trace::CallSet frames;
- /* Whether dependency analysis should be performed. */
- bool dependency_analysis;
-
- /* Whether uninteresting calls should be pruned.. */
- bool prune_uninteresting;
-
/* Output filename */
std::string output;
/* Emit only calls from this thread (-1 == all threads) */
int thread;
-
- /* Print resulting callset */
- int print_callset;
-
- /* What kind of trimming to perform. */
- TrimFlags trim_flags;
};
static int
trim_trace(const char *filename, struct trim_options *options)
{
- trace::ParseBookmark beginning;
trace::Parser p;
- TraceAnalyzer analyzer(options->trim_flags);
- trace::FastCallSet *required;
unsigned frame;
- int call_range_first, call_range_last;
if (!p.open(filename)) {
std::cerr << "error: failed to open " << filename << "\n";
return 1;
}
- /* Mark the beginning so we can return here for pass 2. */
- p.getBookmark(beginning);
-
- /* In pass 1, analyze which calls are needed. */
- frame = 0;
- trace::Call *call;
- while ((call = p.parse_call())) {
-
- /* There's no use doing any work past the last call and frame
- * requested by the user. */
- if ((options->calls.empty() || call->no > options->calls.getLast()) &&
- (options->frames.empty() || frame > options->frames.getLast())) {
-
- delete call;
- break;
- }
-
- /* If requested, ignore all calls not belonging to the specified thread. */
- if (options->thread != -1 && call->thread_id != options->thread) {
- goto NEXT;
- }
-
- /* Also, prune if no side effects (unless the user asked for no pruning. */
- if (options->prune_uninteresting && call->flags & trace::CALL_FLAG_NO_SIDE_EFFECTS) {
- goto NEXT;
- }
-
- /* If this call is included in the user-specified call set,
- * then require it (and all dependencies) in the trimmed
- * output. */
- if (options->calls.contains(*call) ||
- options->frames.contains(frame, call->flags)) {
-
- analyzer.require(call);
- }
-
- /* Regardless of whether we include this call or not, we do
- * some dependency tracking (unless disabled by the user). We
- * do this even for calls we have included in the output so
- * that any state updates get performed. */
- if (options->dependency_analysis) {
- analyzer.analyze(call);
- }
-
- NEXT:
- if (call->flags & trace::CALL_FLAG_END_FRAME)
- frame++;
-
- delete call;
- }
-
/* Prepare output file and writer for output. */
if (options->output.empty()) {
os::String base(filename);
@@ -279,15 +118,9 @@ trim_trace(const char *filename, struct trim_options *options)
return 1;
}
- /* Reset bookmark for pass 2. */
- p.setBookmark(beginning);
-
- /* In pass 2, emit the calls that are required. */
- required = analyzer.get_required();
frame = 0;
- call_range_first = -1;
- call_range_last = -1;
+ trace::Call *call;
while ((call = p.parse_call())) {
/* There's no use doing any work past the last call and frame
@@ -299,23 +132,21 @@ trim_trace(const char *filename, struct trim_options *options)
break;
}
- if (required->contains(call->no)) {
- writer.writeCall(call);
+ /* If requested, ignore all calls not belonging to the specified thread. */
+ if (options->thread != -1 && call->thread_id != options->thread) {
+ goto NEXT;
+ }
+
+ /* If this call is included in the user-specified call set,
+ * then require it (and all dependencies) in the trimmed
+ * output. */
+ if (options->calls.contains(*call) ||
+ options->frames.contains(frame, call->flags)) {
- if (options->print_callset) {
- if (call_range_first < 0) {
- call_range_first = call->no;
- printf ("%d", call_range_first);
- } else if (call->no != call_range_last + 1) {
- if (call_range_last != call_range_first)
- printf ("-%d", call_range_last);
- call_range_first = call->no;
- printf (",%d", call_range_first);
- }
- call_range_last = call->no;
- }
+ writer.writeCall(call);
}
+ NEXT:
if (call->flags & trace::CALL_FLAG_END_FRAME) {
frame++;
}
@@ -323,71 +154,26 @@ trim_trace(const char *filename, struct trim_options *options)
delete call;
}
- if (options->print_callset) {
- if (call_range_last != call_range_first)
- printf ("-%d\n", call_range_last);
- }
-
std::cerr << "Trimmed trace is available as " << options->output << "\n";
return 0;
}
static int
-parse_trim_spec(const char *trim_spec, TrimFlags *flags)
-{
- std::string spec(trim_spec), word;
- size_t start = 0, comma = 0;
- *flags = 0;
-
- while (start < spec.size()) {
- comma = spec.find(',', start);
-
- if (comma == std::string::npos)
- word = std::string(spec, start);
- else
- word = std::string(spec, start, comma - start);
-
- if (strcmp(word.c_str(), "no-side-effects") == 0)
- *flags |= TRIM_FLAG_NO_SIDE_EFFECTS;
- else if (strcmp(word.c_str(), "textures") == 0)
- *flags |= TRIM_FLAG_TEXTURES;
- else if (strcmp(word.c_str(), "shaders") == 0)
- *flags |= TRIM_FLAG_SHADERS;
- else if (strcmp(word.c_str(), "drawing") == 0)
- *flags |= TRIM_FLAG_DRAWING;
- else {
- return 1;
- }
-
- if (comma == std::string::npos)
- break;
-
- start = comma + 1;
- }
-
- return 0;
-}
-
-static int
command(int argc, char *argv[])
{
struct trim_options options;
options.calls = trace::CallSet(trace::FREQUENCY_NONE);
options.frames = trace::CallSet(trace::FREQUENCY_NONE);
- options.dependency_analysis = true;
- options.prune_uninteresting = true;
options.output = "";
options.thread = -1;
- options.print_callset = 0;
- options.trim_flags = -1;
int opt;
while ((opt = getopt_long(argc, argv, shortOptions, longOptions, NULL)) != -1) {
switch (opt) {
case 'h':
- help();
+ usage();
return 0;
case CALLS_OPT:
options.calls.merge(optarg);
@@ -395,42 +181,12 @@ command(int argc, char *argv[])
case FRAMES_OPT:
options.frames.merge(optarg);
break;
- case DEPS_OPT:
- options.dependency_analysis = true;
- break;
- case NO_DEPS_OPT:
- options.dependency_analysis = false;
- break;
- case PRUNE_OPT:
- options.prune_uninteresting = true;
- break;
- case NO_PRUNE_OPT:
- options.prune_uninteresting = false;
- break;
- case 'a':
- options.dependency_analysis = true;
- options.prune_uninteresting = true;
- break;
- case EXACT_OPT:
- options.dependency_analysis = false;
- options.prune_uninteresting = false;
- break;
case THREAD_OPT:
options.thread = atoi(optarg);
break;
case 'o':
options.output = optarg;
break;
- case PRINT_CALLSET_OPT:
- options.print_callset = 1;
- break;
- case TRIM_SPEC_OPT:
- if (parse_trim_spec(optarg, &options.trim_flags)) {
- std::cerr << "error: illegal value for trim-spec: " << optarg << "\n";
- std::cerr << "See \"apitrace help trim\" for help.\n";
- return 1;
- }
- break;
default:
std::cerr << "error: unexpected option `" << (char)opt << "`\n";
usage();
@@ -460,20 +216,12 @@ command(int argc, char *argv[])
return 1;
}
- if (options.dependency_analysis) {
- std::cerr <<
- "Note: The dependency analysis in \"apitrace trim\" is still experimental.\n"
- " We hope that it will be useful, but it may lead to incorrect results.\n"
- " If you find a trace that misbehaves while trimming, please share that\n"
- " by sending email to apitrace@lists.freedesktop.org, cworth@cworth.org\n";
- }
-
return trim_trace(argv[optind], &options);
}
const Command trim_command = {
"trim",
synopsis,
- help,
+ usage,
command
};
diff --git a/cli/cli_trim_auto.cpp b/cli/cli_trim_auto.cpp
new file mode 100644
index 00000000..685de21d
--- /dev/null
+++ b/cli/cli_trim_auto.cpp
@@ -0,0 +1,432 @@
+/**************************************************************************
+ *
+ * Copyright 2010 VMware, Inc.
+ * Copyright 2011 Intel corporation
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#include <sstream>
+#include <string.h>
+#include <limits.h> // for CHAR_MAX
+#include <getopt.h>
+
+#include <set>
+
+#include "cli.hpp"
+
+#include "os_string.hpp"
+
+#include "trace_callset.hpp"
+#include "trace_parser.hpp"
+#include "trace_writer.hpp"
+
+#include "cli_trim_auto_analyzer.hpp"
+
+static const char *synopsis = "Create a new trace by automatically trimming unecessary calls from an existing trace.";
+
+static void
+usage(void)
+{
+ std::cout
+ << "usage: apitrace trim-auto [OPTIONS] TRACE_FILE...\n"
+ << synopsis << "\n"
+ "\n"
+ " -h, --help Show this help message and exit\n"
+ "\n"
+ " --calls=CALLSET Include specified calls in the trimmed output.\n"
+ " --frames=FRAMESET Include specified frames in the trimmed output.\n"
+ "\n"
+ " --deps Perform dependency analysis and include dependent\n"
+ " calls as needed, (even if those calls were not\n"
+ " explicitly requested with --calls or --frames).\n"
+ " (On by default. See --no-deps or --exact)\n"
+ "\n"
+ " --no-deps Do not perform dependency analysis. Output will\n"
+ " not include any additional calls beyond those\n"
+ " explicitly requested with --calls or --frames).\n"
+ "\n"
+ " --prune Omit calls with no side effects, even if the call\n"
+ " is within the range specified by --calls/--frames.\n"
+ " (On by default. See --no-prune or --exact)\n"
+ "\n"
+ " --no-prune Never omit any calls from the range specified\n"
+ " --calls/--frames.\n"
+ "\n"
+ " --print-callset Print to stdout the final set of calls included\n"
+ " in the trim output. This can be useful for\n"
+ " tweaking the trimmed callset from --auto on the\n"
+ " command-line.\n"
+ " Use --calls=@FILE to read callset from a file.\n"
+ "\n"
+ " --trim-spec=SPEC Specifies which classes of calls will be trimmed.\n"
+ " This option only has an effect if dependency\n"
+ " analysis is enabled. The argument is a comma-\n"
+ " separated list of names from the following:\n"
+ "\n"
+ " no-side-effects Calls with no side effects\n"
+ " textures Calls to setup unused textures\n"
+ " shaders Calls to setup unused shaders\n"
+ " drawing Calls that draw\n"
+ "\n"
+ " The default trim specification includes all of\n"
+ " the above, (as much as possible will be trimmed).\n"
+ "\n"
+ " --thread=THREAD_ID Only retain calls from specified thread\n"
+ "\n"
+ " -o, --output=TRACE_FILE Output trace file\n"
+ "\n"
+ ;
+}
+
+enum {
+ CALLS_OPT = CHAR_MAX + 1,
+ FRAMES_OPT,
+ DEPS_OPT,
+ NO_DEPS_OPT,
+ PRUNE_OPT,
+ NO_PRUNE_OPT,
+ THREAD_OPT,
+ PRINT_CALLSET_OPT,
+ TRIM_SPEC_OPT
+};
+
+const static char *
+shortOptions = "ho:x";
+
+const static struct option
+longOptions[] = {
+ {"help", no_argument, 0, 'h'},
+ {"calls", required_argument, 0, CALLS_OPT},
+ {"frames", required_argument, 0, FRAMES_OPT},
+ {"deps", no_argument, 0, DEPS_OPT},
+ {"no-deps", no_argument, 0, NO_DEPS_OPT},
+ {"prune", no_argument, 0, PRUNE_OPT},
+ {"no-prune", no_argument, 0, NO_PRUNE_OPT},
+ {"thread", required_argument, 0, THREAD_OPT},
+ {"output", required_argument, 0, 'o'},
+ {"print-callset", no_argument, 0, PRINT_CALLSET_OPT},
+ {"trim-spec", required_argument, 0, TRIM_SPEC_OPT},
+ {0, 0, 0, 0}
+};
+
+struct stringCompare {
+ bool operator() (const char *a, const char *b) const {
+ return strcmp(a, b) < 0;
+ }
+};
+
+struct trim_auto_options {
+ /* Calls to be included in trace. */
+ trace::CallSet calls;
+
+ /* Frames to be included in trace. */
+ trace::CallSet frames;
+
+ /* Whether dependency analysis should be performed. */
+ bool dependency_analysis;
+
+ /* Whether uninteresting calls should be pruned.. */
+ bool prune_uninteresting;
+
+ /* Output filename */
+ std::string output;
+
+ /* Emit only calls from this thread (-1 == all threads) */
+ int thread;
+
+ /* Print resulting callset */
+ int print_callset;
+
+ /* What kind of trimming to perform. */
+ TrimFlags trim_flags;
+};
+
+static int
+trim_trace(const char *filename, struct trim_auto_options *options)
+{
+ trace::ParseBookmark beginning;
+ trace::Parser p;
+ TraceAnalyzer analyzer(options->trim_flags);
+ trace::FastCallSet *required;
+ unsigned frame;
+ int call_range_first, call_range_last;
+
+ if (!p.open(filename)) {
+ std::cerr << "error: failed to open " << filename << "\n";
+ return 1;
+ }
+
+ /* Mark the beginning so we can return here for pass 2. */
+ p.getBookmark(beginning);
+
+ /* In pass 1, analyze which calls are needed. */
+ frame = 0;
+ trace::Call *call;
+ while ((call = p.parse_call())) {
+
+ /* There's no use doing any work past the last call and frame
+ * requested by the user. */
+ if ((options->calls.empty() || call->no > options->calls.getLast()) &&
+ (options->frames.empty() || frame > options->frames.getLast())) {
+
+ delete call;
+ break;
+ }
+
+ /* If requested, ignore all calls not belonging to the specified thread. */
+ if (options->thread != -1 && call->thread_id != options->thread) {
+ goto NEXT;
+ }
+
+ /* Also, prune if no side effects (unless the user asked for no pruning. */
+ if (options->prune_uninteresting && call->flags & trace::CALL_FLAG_NO_SIDE_EFFECTS) {
+ goto NEXT;
+ }
+
+ /* If this call is included in the user-specified call set,
+ * then require it (and all dependencies) in the trimmed
+ * output. */
+ if (options->calls.contains(*call) ||
+ options->frames.contains(frame, call->flags)) {
+
+ analyzer.require(call);
+ }
+
+ /* Regardless of whether we include this call or not, we do
+ * some dependency tracking (unless disabled by the user). We
+ * do this even for calls we have included in the output so
+ * that any state updates get performed. */
+ if (options->dependency_analysis) {
+ analyzer.analyze(call);
+ }
+
+ NEXT:
+ if (call->flags & trace::CALL_FLAG_END_FRAME)
+ frame++;
+
+ delete call;
+ }
+
+ /* Prepare output file and writer for output. */
+ if (options->output.empty()) {
+ os::String base(filename);
+ base.trimExtension();
+
+ options->output = std::string(base.str()) + std::string("-trim.trace");
+ }
+
+ trace::Writer writer;
+ if (!writer.open(options->output.c_str())) {
+ std::cerr << "error: failed to create " << options->output << "\n";
+ return 1;
+ }
+
+ /* Reset bookmark for pass 2. */
+ p.setBookmark(beginning);
+
+ /* In pass 2, emit the calls that are required. */
+ required = analyzer.get_required();
+
+ frame = 0;
+ call_range_first = -1;
+ call_range_last = -1;
+ while ((call = p.parse_call())) {
+
+ /* There's no use doing any work past the last call and frame
+ * requested by the user. */
+ if ((options->calls.empty() || call->no > options->calls.getLast()) &&
+ (options->frames.empty() || frame > options->frames.getLast())) {
+
+ delete call;
+ break;
+ }
+
+ if (required->contains(call->no)) {
+ writer.writeCall(call);
+
+ if (options->print_callset) {
+ if (call_range_first < 0) {
+ call_range_first = call->no;
+ printf ("%d", call_range_first);
+ } else if (call->no != call_range_last + 1) {
+ if (call_range_last != call_range_first)
+ printf ("-%d", call_range_last);
+ call_range_first = call->no;
+ printf (",%d", call_range_first);
+ }
+ call_range_last = call->no;
+ }
+ }
+
+ if (call->flags & trace::CALL_FLAG_END_FRAME) {
+ frame++;
+ }
+
+ delete call;
+ }
+
+ if (options->print_callset) {
+ if (call_range_last != call_range_first)
+ printf ("-%d\n", call_range_last);
+ }
+
+ std::cerr << "Trimmed trace is available as " << options->output << "\n";
+
+ return 0;
+}
+
+static int
+parse_trim_spec(const char *trim_spec, TrimFlags *flags)
+{
+ std::string spec(trim_spec), word;
+ size_t start = 0, comma = 0;
+ *flags = 0;
+
+ while (start < spec.size()) {
+ comma = spec.find(',', start);
+
+ if (comma == std::string::npos)
+ word = std::string(spec, start);
+ else
+ word = std::string(spec, start, comma - start);
+
+ if (strcmp(word.c_str(), "no-side-effects") == 0)
+ *flags |= TRIM_FLAG_NO_SIDE_EFFECTS;
+ else if (strcmp(word.c_str(), "textures") == 0)
+ *flags |= TRIM_FLAG_TEXTURES;
+ else if (strcmp(word.c_str(), "shaders") == 0)
+ *flags |= TRIM_FLAG_SHADERS;
+ else if (strcmp(word.c_str(), "drawing") == 0)
+ *flags |= TRIM_FLAG_DRAWING;
+ else {
+ return 1;
+ }
+
+ if (comma == std::string::npos)
+ break;
+
+ start = comma + 1;
+ }
+
+ return 0;
+}
+
+static int
+command(int argc, char *argv[])
+{
+ struct trim_auto_options options;
+
+ options.calls = trace::CallSet(trace::FREQUENCY_NONE);
+ options.frames = trace::CallSet(trace::FREQUENCY_NONE);
+ options.dependency_analysis = true;
+ options.prune_uninteresting = true;
+ options.output = "";
+ options.thread = -1;
+ options.print_callset = 0;
+ options.trim_flags = -1;
+
+ int opt;
+ while ((opt = getopt_long(argc, argv, shortOptions, longOptions, NULL)) != -1) {
+ switch (opt) {
+ case 'h':
+ usage();
+ return 0;
+ case CALLS_OPT:
+ options.calls.merge(optarg);
+ break;
+ case FRAMES_OPT:
+ options.frames.merge(optarg);
+ break;
+ case DEPS_OPT:
+ options.dependency_analysis = true;
+ break;
+ case NO_DEPS_OPT:
+ options.dependency_analysis = false;
+ break;
+ case PRUNE_OPT:
+ options.prune_uninteresting = true;
+ break;
+ case NO_PRUNE_OPT:
+ options.prune_uninteresting = false;
+ break;
+ case THREAD_OPT:
+ options.thread = atoi(optarg);
+ break;
+ case 'o':
+ options.output = optarg;
+ break;
+ case PRINT_CALLSET_OPT:
+ options.print_callset = 1;
+ break;
+ case TRIM_SPEC_OPT:
+ if (parse_trim_spec(optarg, &options.trim_flags)) {
+ std::cerr << "error: illegal value for trim-spec: " << optarg << "\n";
+ std::cerr << "See \"apitrace help trim\" for help.\n";
+ return 1;
+ }
+ break;
+ default:
+ std::cerr << "error: unexpected option `" << (char)opt << "`\n";
+ usage();
+ return 1;
+ }
+ }
+
+ /* If neither of --calls nor --frames was set, default to the
+ * entire set of calls. */
+ if (options.calls.empty() && options.frames.empty()) {
+ options.calls = trace::CallSet(trace::FREQUENCY_ALL);
+ }
+
+ if (optind >= argc) {
+ std::cerr << "error: apitrace trim requires a trace file as an argument.\n";
+ usage();
+ return 1;
+ }
+
+ if (argc > optind + 1) {
+ std::cerr << "error: extraneous arguments:";
+ for (int i = optind + 1; i < argc; i++) {
+ std::cerr << " " << argv[i];
+ }
+ std::cerr << "\n";
+ usage();
+ return 1;
+ }
+
+ if (options.dependency_analysis) {
+ std::cerr <<
+ "Note: The dependency analysis in \"apitrace trim-auto\" is still experimental.\n"
+ " We hope that it will be useful, but it may lead to incorrect results.\n"
+ " If you find a trace that misbehaves while trimming, please share that\n"
+ " by sending email to apitrace@lists.freedesktop.org, cworth@cworth.org\n";
+ }
+
+ return trim_trace(argv[optind], &options);
+}
+
+const Command trim_auto_command = {
+ "trim-auto",
+ synopsis,
+ usage,
+ command
+};
diff --git a/cli/trace_analyzer.cpp b/cli/cli_trim_auto_analyzer.cpp
index 79f95579..41d90180 100644
--- a/cli/trace_analyzer.cpp
+++ b/cli/cli_trim_auto_analyzer.cpp
@@ -25,7 +25,7 @@
#include <sstream>
-#include "trace_analyzer.hpp"
+#include "cli_trim_auto_analyzer.hpp"
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define STRNCMP_LITERAL(var, literal) strncmp((var), (literal), sizeof (literal) -1)
diff --git a/cli/trace_analyzer.hpp b/cli/cli_trim_auto_analyzer.hpp
index c344fb6a..c344fb6a 100644
--- a/cli/trace_analyzer.hpp
+++ b/cli/cli_trim_auto_analyzer.hpp
diff --git a/cli/pickle.hpp b/cli/pickle.hpp
index 59af6a7b..eff36e0d 100644
--- a/cli/pickle.hpp
+++ b/cli/pickle.hpp
@@ -27,12 +27,12 @@
* Python pickle writer
*/
-#ifndef _PICKLE_HPP_
-#define _PICKLE_HPP_
+#pragma once
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
+#include <wchar.h>
#include <ostream>
#include <string>
@@ -162,6 +162,23 @@ public:
os.put(TUPLE);
}
+ inline void beginTuple(unsigned length) {
+ if (length >= 4) {
+ os.put(MARK);
+ }
+ }
+
+ inline void endTuple(unsigned length) {
+ static const Opcode ops[4] = {
+ EMPTY_TUPLE,
+ TUPLE1,
+ TUPLE2,
+ TUPLE3,
+ };
+ Opcode op = length < 4 ? ops[length] : TUPLE;
+ os.put(op);
+ }
+
inline void writeString(const char *s, size_t length) {
if (!s) {
writeNone();
@@ -194,6 +211,34 @@ public:
writeString(s.c_str(), s.size());
}
+ inline void writeWString(const wchar_t *s, size_t length) {
+ if (!s) {
+ writeNone();
+ return;
+ }
+
+ /* FIXME: emit UTF-8 */
+ os.put(BINUNICODE);
+ putInt32(length);
+ for (size_t i = 0; i < length; ++i) {
+ wchar_t wc = s[i];
+ char c = wc >= 0 && wc < 0x80 ? (char)wc : '?';
+ os.put(c);
+ }
+
+ os.put(BINPUT);
+ os.put(1);
+ }
+
+ inline void writeWString(const wchar_t *s) {
+ if (!s) {
+ writeNone();
+ return;
+ }
+
+ writeWString(s, wcslen(s));
+ }
+
inline void writeNone(void) {
os.put(NONE);
}
@@ -255,7 +300,7 @@ public:
char c[8];
} u;
- assert(sizeof u.f == sizeof u.c);
+ static_assert(sizeof u.f == sizeof u.c, "double is not 8 bytes");
u.f = f;
os.put(BINFLOAT);
@@ -279,6 +324,16 @@ public:
os.put(REDUCE);
}
+ inline void writePointer(unsigned long long addr) {
+ os.put(GLOBAL);
+ os << "unpickle\nPointer\n";
+ os.put(BINPUT);
+ os.put(1);
+ writeInt(addr);
+ os.put(TUPLE1);
+ os.put(REDUCE);
+ }
+
protected:
inline void putInt16(uint16_t i) {
os.put( i & 0xff);
@@ -330,4 +385,3 @@ protected:
}
};
-#endif /* _Pickle_HPP_ */