diff --git a/capture/build/unix/Makefile b/capture/build/unix/Makefile new file mode 100644 index 00000000..3b50301c --- /dev/null +++ b/capture/build/unix/Makefile @@ -0,0 +1,12 @@ +all: debug + +debug: + @+make -f debug.mk all + +release: + @+make -f release.mk all + +clean: + @+make -f build.mk clean + +.PHONY: all clean debug release diff --git a/capture/build/unix/build.mk b/capture/build/unix/build.mk new file mode 100644 index 00000000..4196005b --- /dev/null +++ b/capture/build/unix/build.mk @@ -0,0 +1,51 @@ +CFLAGS += +CXXFLAGS := $(CFLAGS) -std=gnu++14 +DEFINES += +INCLUDES := +LIBS := +IMAGE := capture + +FILTER := + +BASE := $(shell egrep 'ClCompile.*cpp"' ../win32/$(IMAGE).vcxproj | sed -e 's/.*\"\(.*\)\".*/\1/' | sed -e 's@\\@/@g') +BASE2 := $(shell egrep 'ClCompile.*c"' ../win32/$(IMAGE).vcxproj | sed -e 's/.*\"\(.*\)\".*/\1/' | sed -e 's@\\@/@g') + +SRC := $(filter-out $(FILTER),$(BASE)) +SRC2 := $(filter-out $(FILTER),$(BASE2)) + +OBJ := $(SRC:%.cpp=%.o) +OBJ2 := $(SRC2:%.c=%.o) + +all: $(IMAGE) + +%.o: %.cpp + $(CXX) -c $(INCLUDES) $(CXXFLAGS) $(DEFINES) $< -o $@ + +%.d : %.cpp + @echo Resolving dependencies of $< + @mkdir -p $(@D) + @$(CXX) -MM $(INCLUDES) $(CXXFLAGS) $(DEFINES) $< > $@.$$$$; \ + sed 's,.*\.o[ :]*,$(<:.cpp=.o) $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +%.o: %.c + $(CC) -c $(INCLUDES) $(CFLAGS) $(DEFINES) $< -o $@ + +%.d : %.c + @echo Resolving dependencies of $< + @mkdir -p $(@D) + @$(CC) -MM $(INCLUDES) $(CFLAGS) $(DEFINES) $< > $@.$$$$; \ + sed 's,.*\.o[ :]*,$(<:.c=.o) $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +$(IMAGE): $(OBJ) $(OBJ2) + $(CXX) $(CXXFLAGS) $(DEFINES) $(OBJ) $(OBJ2) $(LIBS) -o $@ + +ifneq "$(MAKECMDGOALS)" "clean" +-include $(SRC:.cpp=.d) $(SRC2:.c=.d) +endif + +clean: + rm -f $(OBJ) $(OBJ2) $(SRC:.cpp=.d) $(SRC2:.c=.d) $(IMAGE) + +.PHONY: clean all diff --git a/capture/build/unix/debug.mk b/capture/build/unix/debug.mk new file mode 100644 index 00000000..5a4e17b3 --- /dev/null +++ b/capture/build/unix/debug.mk @@ -0,0 +1,10 @@ +ARCH := $(shell uname -m) + +CFLAGS := -g3 -Wall +DEFINES := -DDEBUG + +ifeq ($(ARCH),x86_64) +CFLAGS += -msse4.1 +endif + +include build.mk diff --git a/capture/build/unix/release.mk b/capture/build/unix/release.mk new file mode 100644 index 00000000..1c820a6e --- /dev/null +++ b/capture/build/unix/release.mk @@ -0,0 +1,10 @@ +ARCH := $(shell uname -m) + +CFLAGS := -O3 -s -fomit-frame-pointer +DEFINES := -DNDEBUG + +ifeq ($(ARCH),x86_64) +CFLAGS += -msse4.1 +endif + +include build.mk diff --git a/capture/build/win32/capture.sln b/capture/build/win32/capture.sln new file mode 100644 index 00000000..aec6a2e0 --- /dev/null +++ b/capture/build/win32/capture.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27428.2002 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "capture", "capture.vcxproj", "{447D58BF-94CD-4469-BB90-549C05D03E00}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {447D58BF-94CD-4469-BB90-549C05D03E00}.Debug|x64.ActiveCfg = Debug|x64 + {447D58BF-94CD-4469-BB90-549C05D03E00}.Debug|x64.Build.0 = Debug|x64 + {447D58BF-94CD-4469-BB90-549C05D03E00}.Release|x64.ActiveCfg = Release|x64 + {447D58BF-94CD-4469-BB90-549C05D03E00}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3E51386C-43EA-44AC-9F24-AFAFE4D63ADE} + EndGlobalSection +EndGlobal diff --git a/capture/build/win32/capture.vcxproj b/capture/build/win32/capture.vcxproj new file mode 100644 index 00000000..39712627 --- /dev/null +++ b/capture/build/win32/capture.vcxproj @@ -0,0 +1,163 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {447D58BF-94CD-4469-BB90-549C05D03E00} + capture + 10.0.16299.0 + + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + true + true + + + + + Level3 + Disabled + true + true + true + _CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;WIN32_LEAN_AND_MEAN;NOMINMAX;_USE_MATH_DEFINES;%(PreprocessorDefinitions) + AdvancedVectorExtensions2 + + + ws2_32.lib;%(AdditionalDependencies) + Console + + + + + Level3 + MaxSpeed + true + true + true + true + + + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + true + NDEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;WIN32_LEAN_AND_MEAN;NOMINMAX;_USE_MATH_DEFINES;%(PreprocessorDefinitions) + AdvancedVectorExtensions2 + + + true + true + ws2_32.lib;%(AdditionalDependencies) + Console + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/capture/build/win32/capture.vcxproj.filters b/capture/build/win32/capture.vcxproj.filters new file mode 100644 index 00000000..3bc2972c --- /dev/null +++ b/capture/build/win32/capture.vcxproj.filters @@ -0,0 +1,99 @@ + + + + + {729c80ee-4d26-4a5e-8f1f-6c075783eb56} + + + {cf23ef7b-7694-4154-830b-00cf053350ea} + + + {e39d3623-47cd-4752-8da9-3ea324f964c1} + + + + + common + + + common + + + common + + + server + + + server + + + src + + + src + + + + + common + + + common + + + common + + + common + + + common + + + common + + + common + + + common + + + server + + + server + + + server + + + server + + + server + + + server + + + server + + + server + + + server + + + server + + + server + + + src + + + \ No newline at end of file diff --git a/capture/src/capture.cpp b/capture/src/capture.cpp new file mode 100644 index 00000000..101c3d9d --- /dev/null +++ b/capture/src/capture.cpp @@ -0,0 +1,97 @@ +#ifdef _WIN32 +# include +#endif + +#include +#include +#include +#include + +#include "../../server/tracy_benaphore.h" +#include "../../server/TracyFileWrite.hpp" +#include "../../server/TracyMemory.hpp" +#include "../../server/TracyWorker.hpp" +#include "getopt.h" + +void Usage() +{ + printf( "Usage: capture -a address -o output.tracy\n" ); + exit( 1 ); +} + +int main( int argc, char** argv ) +{ +#ifdef _WIN32 + if( !AttachConsole( ATTACH_PARENT_PROCESS ) ) + { + AllocConsole(); + SetConsoleMode( GetStdHandle( STD_OUTPUT_HANDLE ), 0x07 ); + } +#endif + + const char* address = nullptr; + const char* output = nullptr; + + int c; + while( ( c = getopt( argc, argv, "a:o:" ) ) != -1 ) + { + switch( c ) + { + case 'a': + address = optarg; + break; + case 'o': + output = optarg; + break; + default: + Usage(); + break; + } + } + + if( !address || !output ) Usage(); + + printf( "Connecting to %s...", address ); + fflush( stdout ); + tracy::Worker worker( address ); + while( !worker.HasData() ) std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) ); + printf( "\n" ); + + auto& lock = worker.GetMbpsDataLock(); + + while( worker.IsConnected() ) + { + lock.lock(); + const auto mbps = worker.GetMbpsData().back(); + const auto compRatio = worker.GetCompRatio(); + lock.unlock(); + + if( mbps < 0.1f ) + { + printf( "\33[2K\r\033[36;1m%7.2f Kbps", mbps * 1000.f ); + } + else + { + printf( "\33[2K\r\033[36;1m%7.2f Mbps", mbps ); + } + printf( " \033[0m| Ratio: \033[36;1m%5.1f%% \033[0m| Real: \033[33;1m%7.2f Mbps \033[0m| Mem: \033[31;1m%.2f MB\033[0m", compRatio * 100.f, mbps / compRatio, tracy::memUsage.load( std::memory_order_relaxed ) / ( 1024.f * 1024.f ) ); + fflush( stdout ); + + std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) ); + } + + printf( "\nSaving trace..." ); + fflush( stdout ); + auto f = tracy::FileWrite::Open( output ); + if( f ) + { + worker.Write( *f ); + printf( " \033[32;1mdone!\033[0m\n" ); + } + else + { + printf( " \033[31;1failed!\033[0m\n" ); + } + + return 0; +} diff --git a/capture/src/getopt.c b/capture/src/getopt.c new file mode 100644 index 00000000..eae36a64 --- /dev/null +++ b/capture/src/getopt.c @@ -0,0 +1,228 @@ +/******************************************************************************* + * Copyright (c) 2012-2017, Kim Grasman + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Kim Grasman nor the + * names of contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL KIM GRASMAN BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#include "getopt.h" + +#include +#include + +char* optarg; +int optopt; +/* The variable optind [...] shall be initialized to 1 by the system. */ +int optind = 1; +int opterr; + +static char* optcursor = NULL; + +/* Implemented based on [1] and [2] for optional arguments. + optopt is handled FreeBSD-style, per [3]. + Other GNU and FreeBSD extensions are purely accidental. + +[1] http://pubs.opengroup.org/onlinepubs/000095399/functions/getopt.html +[2] http://www.kernel.org/doc/man-pages/online/pages/man3/getopt.3.html +[3] http://www.freebsd.org/cgi/man.cgi?query=getopt&sektion=3&manpath=FreeBSD+9.0-RELEASE +*/ +int getopt(int argc, char* const argv[], const char* optstring) { + int optchar = -1; + const char* optdecl = NULL; + + optarg = NULL; + opterr = 0; + optopt = 0; + + /* Unspecified, but we need it to avoid overrunning the argv bounds. */ + if (optind >= argc) + goto no_more_optchars; + + /* If, when getopt() is called argv[optind] is a null pointer, getopt() + shall return -1 without changing optind. */ + if (argv[optind] == NULL) + goto no_more_optchars; + + /* If, when getopt() is called *argv[optind] is not the character '-', + getopt() shall return -1 without changing optind. */ + if (*argv[optind] != '-') + goto no_more_optchars; + + /* If, when getopt() is called argv[optind] points to the string "-", + getopt() shall return -1 without changing optind. */ + if (strcmp(argv[optind], "-") == 0) + goto no_more_optchars; + + /* If, when getopt() is called argv[optind] points to the string "--", + getopt() shall return -1 after incrementing optind. */ + if (strcmp(argv[optind], "--") == 0) { + ++optind; + goto no_more_optchars; + } + + if (optcursor == NULL || *optcursor == '\0') + optcursor = argv[optind] + 1; + + optchar = *optcursor; + + /* FreeBSD: The variable optopt saves the last known option character + returned by getopt(). */ + optopt = optchar; + + /* The getopt() function shall return the next option character (if one is + found) from argv that matches a character in optstring, if there is + one that matches. */ + optdecl = strchr(optstring, optchar); + if (optdecl) { + /* [I]f a character is followed by a colon, the option takes an + argument. */ + if (optdecl[1] == ':') { + optarg = ++optcursor; + if (*optarg == '\0') { + /* GNU extension: Two colons mean an option takes an + optional arg; if there is text in the current argv-element + (i.e., in the same word as the option name itself, for example, + "-oarg"), then it is returned in optarg, otherwise optarg is set + to zero. */ + if (optdecl[2] != ':') { + /* If the option was the last character in the string pointed to by + an element of argv, then optarg shall contain the next element + of argv, and optind shall be incremented by 2. If the resulting + value of optind is greater than argc, this indicates a missing + option-argument, and getopt() shall return an error indication. + + Otherwise, optarg shall point to the string following the + option character in that element of argv, and optind shall be + incremented by 1. + */ + if (++optind < argc) { + optarg = argv[optind]; + } else { + /* If it detects a missing option-argument, it shall return the + colon character ( ':' ) if the first character of optstring + was a colon, or a question-mark character ( '?' ) otherwise. + */ + optarg = NULL; + optchar = (optstring[0] == ':') ? ':' : '?'; + } + } else { + optarg = NULL; + } + } + + optcursor = NULL; + } + } else { + /* If getopt() encounters an option character that is not contained in + optstring, it shall return the question-mark ( '?' ) character. */ + optchar = '?'; + } + + if (optcursor == NULL || *++optcursor == '\0') + ++optind; + + return optchar; + +no_more_optchars: + optcursor = NULL; + return -1; +} + +/* Implementation based on [1]. + +[1] http://www.kernel.org/doc/man-pages/online/pages/man3/getopt.3.html +*/ +int getopt_long(int argc, char* const argv[], const char* optstring, + const struct option* longopts, int* longindex) { + const struct option* o = longopts; + const struct option* match = NULL; + int num_matches = 0; + size_t argument_name_length = 0; + const char* current_argument = NULL; + int retval = -1; + + optarg = NULL; + optopt = 0; + + if (optind >= argc) + return -1; + + if (strlen(argv[optind]) < 3 || strncmp(argv[optind], "--", 2) != 0) + return getopt(argc, argv, optstring); + + /* It's an option; starts with -- and is longer than two chars. */ + current_argument = argv[optind] + 2; + argument_name_length = strcspn(current_argument, "="); + for (; o->name; ++o) { + if (strncmp(o->name, current_argument, argument_name_length) == 0) { + match = o; + ++num_matches; + } + } + + if (num_matches == 1) { + /* If longindex is not NULL, it points to a variable which is set to the + index of the long option relative to longopts. */ + if (longindex) + *longindex = (match - longopts); + + /* If flag is NULL, then getopt_long() shall return val. + Otherwise, getopt_long() returns 0, and flag shall point to a variable + which shall be set to val if the option is found, but left unchanged if + the option is not found. */ + if (match->flag) + *(match->flag) = match->val; + + retval = match->flag ? 0 : match->val; + + if (match->has_arg != no_argument) { + optarg = strchr(argv[optind], '='); + if (optarg != NULL) + ++optarg; + + if (match->has_arg == required_argument) { + /* Only scan the next argv for required arguments. Behavior is not + specified, but has been observed with Ubuntu and Mac OSX. */ + if (optarg == NULL && ++optind < argc) { + optarg = argv[optind]; + } + + if (optarg == NULL) + retval = ':'; + } + } else if (strchr(argv[optind], '=')) { + /* An argument was provided to a non-argument option. + I haven't seen this specified explicitly, but both GNU and BSD-based + implementations show this behavior. + */ + retval = '?'; + } + } else { + /* Unknown option or ambiguous match. */ + retval = '?'; + } + + ++optind; + return retval; +} diff --git a/capture/src/getopt.h b/capture/src/getopt.h new file mode 100644 index 00000000..a1d6db03 --- /dev/null +++ b/capture/src/getopt.h @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2012-2017, Kim Grasman + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Kim Grasman nor the + * names of contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL KIM GRASMAN BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef INCLUDED_GETOPT_PORT_H +#define INCLUDED_GETOPT_PORT_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#define no_argument 1 +#define required_argument 2 +#define optional_argument 3 + +extern char* optarg; +extern int optind, opterr, optopt; + +struct option { + const char* name; + int has_arg; + int* flag; + int val; +}; + +int getopt(int argc, char* const argv[], const char* optstring); + +int getopt_long(int argc, char* const argv[], + const char* optstring, const struct option* longopts, int* longindex); + +#if defined(__cplusplus) +} +#endif + +#endif // INCLUDED_GETOPT_PORT_H