mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-11-21 21:17:19 +00:00
[profile] added a tool to help profile the host capture perf
This commit is contained in:
parent
2d9f578719
commit
85d46ed2b0
@ -401,6 +401,7 @@ static int frameThread(void * unused)
|
|||||||
updatePositionInfo();
|
updatePositionInfo();
|
||||||
}
|
}
|
||||||
FrameBuffer frame = (FrameBuffer)((uint8_t *)state.shm + header.dataPos);
|
FrameBuffer frame = (FrameBuffer)((uint8_t *)state.shm + header.dataPos);
|
||||||
|
|
||||||
if (!state.lgr->on_frame_event(state.lgrData, lgrFormat, frame))
|
if (!state.lgr->on_frame_event(state.lgrData, lgrFormat, frame))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("renderer on frame event returned failure");
|
DEBUG_ERROR("renderer on frame event returned failure");
|
||||||
|
5
profile/README.md
Normal file
5
profile/README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
##This directory contains programs for LG performance profiling.
|
||||||
|
|
||||||
|
###Directories:
|
||||||
|
|
||||||
|
* `client` - dummy client that profiles the host application's performance.
|
3
profile/client/.gitignore
vendored
Normal file
3
profile/client/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
bin/
|
||||||
|
build/
|
||||||
|
*.swp
|
64
profile/client/CMakeLists.txt
Normal file
64
profile/client/CMakeLists.txt
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
project(profiler-client C)
|
||||||
|
|
||||||
|
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
|
||||||
|
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
include(CheckCCompilerFlag)
|
||||||
|
include(FeatureSummary)
|
||||||
|
|
||||||
|
option(OPTIMIZE_FOR_NATIVE "Build with -march=native" ON)
|
||||||
|
if(OPTIMIZE_FOR_NATIVE)
|
||||||
|
CHECK_C_COMPILER_FLAG("-march=native" COMPILER_SUPPORTS_MARCH_NATIVE)
|
||||||
|
if(COMPILER_SUPPORTS_MARCH_NATIVE)
|
||||||
|
add_compile_options("-march=native")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_compile_options(
|
||||||
|
"-Wall"
|
||||||
|
"-Werror"
|
||||||
|
"-Wfatal-errors"
|
||||||
|
"-ffast-math"
|
||||||
|
"-fdata-sections"
|
||||||
|
"-ffunction-sections"
|
||||||
|
"$<$<CONFIG:DEBUG>:-O0;-g3;-ggdb>"
|
||||||
|
)
|
||||||
|
|
||||||
|
set(EXE_FLAGS "-Wl,--gc-sections")
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND cat ../../VERSION
|
||||||
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
|
OUTPUT_VARIABLE BUILD_VERSION
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
|
||||||
|
add_definitions(-D BUILD_VERSION='"${BUILD_VERSION}"')
|
||||||
|
get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/../.." ABSOLUTE)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${PROJECT_SOURCE_DIR}/include
|
||||||
|
${CMAKE_BINARY_DIR}/include
|
||||||
|
)
|
||||||
|
|
||||||
|
link_libraries(
|
||||||
|
rt
|
||||||
|
m
|
||||||
|
)
|
||||||
|
|
||||||
|
set(SOURCES
|
||||||
|
src/main.c
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory("${PROJECT_TOP}/common" "${CMAKE_BINARY_DIR}/common")
|
||||||
|
|
||||||
|
add_executable(profiler-client ${SOURCES})
|
||||||
|
target_compile_options(profiler-client PUBLIC ${PKGCONFIG_CFLAGS_OTHER})
|
||||||
|
target_link_libraries(profiler-client
|
||||||
|
${EXE_FLAGS}
|
||||||
|
lg_common
|
||||||
|
)
|
||||||
|
|
||||||
|
feature_summary(WHAT ENABLED_FEATURES DISABLED_FEATURES)
|
303
profile/client/src/main.c
Normal file
303
profile/client/src/main.c
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
/*
|
||||||
|
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||||
|
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||||
|
https://looking-glass.hostfission.com
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free Software
|
||||||
|
Foundation; either version 2 of the License, or (at your option) any later
|
||||||
|
version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with
|
||||||
|
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||||
|
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common/debug.h"
|
||||||
|
#include "common/option.h"
|
||||||
|
#include "common/crash.h"
|
||||||
|
#include "common/KVMFR.h"
|
||||||
|
#include "common/locking.h"
|
||||||
|
#include "common/stringutils.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#define min(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a < _b ? _a : _b; })
|
||||||
|
#define max(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a > _b ? _a : _b; })
|
||||||
|
|
||||||
|
struct state
|
||||||
|
{
|
||||||
|
int shmSize;
|
||||||
|
int shmFD;
|
||||||
|
struct KVMFRHeader * shm;
|
||||||
|
|
||||||
|
bool running;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct state state;
|
||||||
|
|
||||||
|
static struct Option options[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
.module = "app",
|
||||||
|
.name = "configFile",
|
||||||
|
.description = "A file to read additional configuration from",
|
||||||
|
.shortopt = 'C',
|
||||||
|
.type = OPTION_TYPE_STRING,
|
||||||
|
.value.x_string = NULL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.module = "app",
|
||||||
|
.name = "shmFile",
|
||||||
|
.description = "The path to the shared memory file",
|
||||||
|
.shortopt = 'f',
|
||||||
|
.type = OPTION_TYPE_STRING,
|
||||||
|
.value.x_string = "/dev/shm/looking-glass"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.module = "app",
|
||||||
|
.name = "shmSize",
|
||||||
|
.description = "Specify the size in MB of the shared memory file (0 = detect)",
|
||||||
|
.shortopt = 'L',
|
||||||
|
.type = OPTION_TYPE_INT,
|
||||||
|
.value.x_int = 0
|
||||||
|
},
|
||||||
|
{0}
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool config_load(int argc, char * argv[])
|
||||||
|
{
|
||||||
|
// load any global options first
|
||||||
|
struct stat st;
|
||||||
|
if (stat("/etc/looking-glass-client.ini", &st) >= 0)
|
||||||
|
{
|
||||||
|
DEBUG_INFO("Loading config from: /etc/looking-glass-client.ini");
|
||||||
|
if (!option_load("/etc/looking-glass-client.ini"))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// load user's local options
|
||||||
|
struct passwd * pw = getpwuid(getuid());
|
||||||
|
char * localFile;
|
||||||
|
alloc_sprintf(&localFile, "%s/.looking-glass-client.ini", pw->pw_dir);
|
||||||
|
if (stat(localFile, &st) >= 0)
|
||||||
|
{
|
||||||
|
DEBUG_INFO("Loading config from: %s", localFile);
|
||||||
|
if (!option_load(localFile))
|
||||||
|
{
|
||||||
|
free(localFile);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(localFile);
|
||||||
|
|
||||||
|
if (!option_parse(argc, argv))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// if a file was specified to also load, do it
|
||||||
|
const char * configFile = option_get_string("app", "configFile");
|
||||||
|
if (configFile)
|
||||||
|
{
|
||||||
|
DEBUG_INFO("Loading config from: %s", configFile);
|
||||||
|
if (!option_load(configFile))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!option_validate())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool map_memory()
|
||||||
|
{
|
||||||
|
const char * shmFile = option_get_string("app", "shmFile");
|
||||||
|
int shmSize = option_get_int ("app", "shmSize");
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
if (stat(shmFile, &st) < 0)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to stat the shared memory file: %s", shmFile);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.shmSize = shmSize ? shmSize : st.st_size;
|
||||||
|
state.shmFD = open(shmFile, O_RDWR, (mode_t)0600);
|
||||||
|
if (state.shmFD < 0)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to open the shared memory file: %s", shmFile);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.shm = (struct KVMFRHeader *)mmap(0, shmSize, PROT_READ | PROT_WRITE, MAP_SHARED, state.shmFD, 0);
|
||||||
|
if (state.shm == MAP_FAILED)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to map the shared memory file: %s", shmFile);
|
||||||
|
state.shm = NULL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_INFO("Shared memory mapped @ 0x%p", state.shm);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unmap_memory()
|
||||||
|
{
|
||||||
|
if (state.shmFD > -1)
|
||||||
|
{
|
||||||
|
if (state.shm)
|
||||||
|
{
|
||||||
|
munmap(state.shm, state.shmSize);
|
||||||
|
state.shm = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(state.shmFD);
|
||||||
|
state.shmFD = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64_t nanotime()
|
||||||
|
{
|
||||||
|
struct timespec time;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC_RAW, &time);
|
||||||
|
return ((uint64_t)time.tv_sec * 1e9) + time.tv_nsec;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int run()
|
||||||
|
{
|
||||||
|
DEBUG_INFO("Waiting for host to signal it's ready...");
|
||||||
|
__sync_or_and_fetch(&state.shm->flags, KVMFR_HEADER_FLAG_RESTART);
|
||||||
|
|
||||||
|
while(state.running && (state.shm->flags & KVMFR_HEADER_FLAG_RESTART))
|
||||||
|
usleep(1e6);
|
||||||
|
|
||||||
|
if (!state.running)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
DEBUG_INFO("Host ready, starting session");
|
||||||
|
|
||||||
|
// check the header's magic and version are valid
|
||||||
|
if (memcmp(state.shm->magic, KVMFR_HEADER_MAGIC, sizeof(KVMFR_HEADER_MAGIC)) != 0)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Invalid header magic, is the host running?");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_INFO("KVMFR Version: %u", state.shm->version);
|
||||||
|
if (state.shm->version != KVMFR_HEADER_VERSION)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("KVMFR version missmatch, expected %u but got %u", KVMFR_HEADER_VERSION, state.shm->version);
|
||||||
|
DEBUG_ERROR("This is not a bug, ensure you have the right version of looking-glass-host.exe on the guest");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct perf
|
||||||
|
{
|
||||||
|
uint64_t min, max, ttl;
|
||||||
|
unsigned int count;
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned int frameCount = 0;
|
||||||
|
uint64_t lastFrameTime = 0;
|
||||||
|
struct perf p1 = {};
|
||||||
|
struct perf p5 = {};
|
||||||
|
struct perf p10 = {};
|
||||||
|
struct perf p30 = {};
|
||||||
|
|
||||||
|
// start accepting frames
|
||||||
|
while(state.running)
|
||||||
|
{
|
||||||
|
// we don't sleep in this loop as we are profiling the host application
|
||||||
|
while(!(state.shm->frame.flags & KVMFR_FRAME_FLAG_UPDATE))
|
||||||
|
{
|
||||||
|
if (!state.running)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t frameTime = nanotime();
|
||||||
|
|
||||||
|
// tell the host that it can continue
|
||||||
|
__sync_and_and_fetch(&state.shm->frame.flags, ~KVMFR_FRAME_FLAG_UPDATE);
|
||||||
|
|
||||||
|
uint64_t diff = frameTime - lastFrameTime;
|
||||||
|
|
||||||
|
if (frameCount++ == 0)
|
||||||
|
{
|
||||||
|
lastFrameTime = frameTime;
|
||||||
|
p1.min = p5.min = p10.min = p30.min = diff;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
++p1 .count;
|
||||||
|
++p5 .count;
|
||||||
|
++p10.count;
|
||||||
|
++p30.count;
|
||||||
|
|
||||||
|
#define UPDATE(p, interval) \
|
||||||
|
if (p.ttl + diff >= (1e9 * interval)) \
|
||||||
|
{ \
|
||||||
|
fprintf(stdout, "%02d, min:%9lu ns (%5.2f ms) max:%9lu ns (%5.2f ms) avg:%9lu ns (%5.2f ms)\n", \
|
||||||
|
interval, \
|
||||||
|
p.min , ((float)p.min / 1e6f), \
|
||||||
|
p.max , ((float)p.max / 1e6f), \
|
||||||
|
p.ttl / p.count, (((float)p.ttl / p.count) / 1e6f)\
|
||||||
|
); \
|
||||||
|
p.min = p.max = p.ttl = diff; p.count = 1; \
|
||||||
|
} \
|
||||||
|
else \
|
||||||
|
{ \
|
||||||
|
p.min = min(p.min, diff); \
|
||||||
|
p.max = max(p.max, diff); \
|
||||||
|
p.ttl += diff; \
|
||||||
|
}
|
||||||
|
|
||||||
|
UPDATE(p1 , 1 );
|
||||||
|
UPDATE(p5 , 5 );
|
||||||
|
UPDATE(p10, 10);
|
||||||
|
UPDATE(p30, 30);
|
||||||
|
|
||||||
|
lastFrameTime = frameTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char * argv[])
|
||||||
|
{
|
||||||
|
DEBUG_INFO("Looking Glass (" BUILD_VERSION ") - Client Profiler");
|
||||||
|
|
||||||
|
if (!installCrashHandler("/proc/self/exe"))
|
||||||
|
DEBUG_WARN("Failed to install the crash handler");
|
||||||
|
|
||||||
|
option_register(options);
|
||||||
|
|
||||||
|
if (!config_load(argc, argv))
|
||||||
|
{
|
||||||
|
option_free();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init the global state vars
|
||||||
|
state.shmFD = -1;
|
||||||
|
state.shm = NULL;
|
||||||
|
state.running = true;
|
||||||
|
|
||||||
|
int ret = -1;
|
||||||
|
if (map_memory())
|
||||||
|
ret = run();
|
||||||
|
|
||||||
|
unmap_memory();
|
||||||
|
option_free();
|
||||||
|
return ret;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user