diff --git a/CMakeLists.txt b/CMakeLists.txt index ad4b7e62..451daef2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,15 +71,21 @@ install(TARGETS ${PROJECT_NAME} PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ COMPONENT sdl_atf) +install(DIRECTORY "${CMAKE_SOURCE_DIR}/remote_atf_docker" + "${CMAKE_SOURCE_DIR}/data" + DESTINATION "${CMAKE_INSTALL_PREFIX}" + USE_SOURCE_PERMISSIONS + COMPONENT sdl_atf) + install(DIRECTORY "${CMAKE_SOURCE_DIR}/modules" "${CMAKE_SOURCE_DIR}/data" DESTINATION "${CMAKE_INSTALL_PREFIX}" - FILE_PERMISSIONS OWNER_WRITE OWNER_READ + USE_SOURCE_PERMISSIONS COMPONENT sdl_atf) install(DIRECTORY "${CMAKE_SOURCE_DIR}/tools" DESTINATION "${CMAKE_INSTALL_PREFIX}" - FILE_PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ + USE_SOURCE_PERMISSIONS COMPONENT sdl_atf) install(FILES "${CMAKE_SOURCE_DIR}/start.sh" diff --git a/remote_atf_docker/Dockerfile b/remote_atf_docker/Dockerfile new file mode 100644 index 00000000..478341c0 --- /dev/null +++ b/remote_atf_docker/Dockerfile @@ -0,0 +1,29 @@ +FROM ubuntu:18.04 + +ENV TZ=Europe/Minsk +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN apt-get update && apt-get -q -y install locales sudo + +RUN apt-get update && apt-get -q -y install libssl1.0.0 libusb-1.0-0 libbluetooth3 \ + libssl-doc- libusb-1.0-doc- manpages- manpages-dev- + +RUN apt-get update && apt-get -q -y install openssl liblua5.2-0 psmisc \ + autotools-dev- binutils- build-essential- bzip2- cpp- cpp-5- dpkg-dev- fakeroot- \ + manpages- manpages-dev- + +RUN apt-get update && apt-get -q -y install sqlite3 libqt5websockets5 net-tools iproute2 \ + qttranslations5-l10n- xdg-user-dirs- xml-core- dbus- + +RUN apt-get update && apt install -y netcat cpipe gnuplot sysstat python3-pip docker.io +RUN apt-get update && apt install -y python3-numpy python3-scipy python3-matplotlib + +RUN locale-gen en_US.UTF-8 && update-locale LANG=en_US.UTF-8 + +ENV LC_ALL en_US.UTF-8 + +RUN chmod u+s /sbin/ifconfig /sbin/ip && mkdir /home/developer + +COPY entrypoint.sh /usr/bin/ + +ENTRYPOINT ["/bin/bash", "-e", "/usr/bin/entrypoint.sh"] diff --git a/remote_atf_docker/build_docker.sh b/remote_atf_docker/build_docker.sh new file mode 100755 index 00000000..7ba5abd0 --- /dev/null +++ b/remote_atf_docker/build_docker.sh @@ -0,0 +1 @@ +docker build . -t remote_atf_server diff --git a/remote_atf_docker/entrypoint.sh b/remote_atf_docker/entrypoint.sh new file mode 100755 index 00000000..e7a4f3e8 --- /dev/null +++ b/remote_atf_docker/entrypoint.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +# Add local user +# Either use the LOCAL_USER_ID if passed in at runtime or +# fallback + +USER_ID=${LOCAL_USER_ID:-9001} + +useradd --shell /bin/bash -u $USER_ID -o -c "" developer +usermod -aG docker developer +sudo chmod 666 /var/run/docker.sock +echo "developer ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + +chown developer /home/developer +chgrp developer /home/developer + +export HOME=/home/developer +export LANG=en_US.UTF-8 + +echo "Remote adapter server available by following ip adresses : " +ip addr | grep -E "inet " | tr -s " " | cut -d ' ' -f 3| cut -d '/' -f 1 + +echo " ls /home/developer " +ls /home/developer + +echo "SDL bin dir : /home/developer/sdlbin"; ls -la /home/developer/sdlbin +echo "ATF bin dir : /home/developer/atfbin"; ls -la /home/developer/atfbin +echo "Third party /home/developer/thirdparty"; ls -la /home/developer/thirdparty + +cd /home/developer/atfbin/RemoteTestingAdapterServer + +echo "Third party contents" +LIB="/home/developer/thirdparty/lib/" +LIB64="/home/developer/thirdparty/x86_64/lib/" + +sudo LD_LIBRARY_PATH="$LIB:$LIB64:." -u developer "./RemoteTestingAdapterServer" + diff --git a/remote_atf_docker/start_remote_server.sh b/remote_atf_docker/start_remote_server.sh new file mode 100755 index 00000000..320ea855 --- /dev/null +++ b/remote_atf_docker/start_remote_server.sh @@ -0,0 +1,37 @@ +PATH_TO_SDLBIN=$1 +PATH_TO_ATFBIN=$2 +PATH_TO_THIRDPARTY=$3 + +if [ -z "$PATH_TO_SDLBIN" ]; then + echo "Path to SDL binaries is not defined as 1st argument" + exit 1; +fi + +if [ -z "$PATH_TO_ATFBIN" ]; then + echo "Path to ATF binaries is not defined as 2nd argument" + exit 1; +fi + +if [ -z "$PATH_TO_THIRDPARTY" ]; then + echo "Path to 3rd party libraries is not defined as 3rd argument" + exit 1; +fi + +CONTAINER_NAME=remote_sdl +IMAGE_NAME=remote_atf_server +MEMORY_CONSTRAINS=4G +CPUS=2 +docker rm $CONTAINER_NAME + +echo $1 $2 $3 +docker run -e LOCAL_USER_ID=$UID \ + -e CONTAINER_NAME=$CONTAINER_NAME\ + -e LOCAL_USER_ID=$(id -u)\ + --net=host\ + --name=$CONTAINER_NAME\ + -v /var/run/docker.sock:/var/run/docker.sock\ + -v $PATH_TO_SDLBIN:/home/developer/sdlbin\ + -v $PATH_TO_ATFBIN:/home/developer/atfbin\ + -v $PATH_TO_THIRDPARTY:/home/developer/thirdparty\ + -m=$MEMORY_CONSTRAINS\ + -it $IMAGE_NAME diff --git a/src/remote_adapter/remote_adapter_server/BOOST.cmake b/src/remote_adapter/remote_adapter_server/BOOST.cmake index 1b2a190c..b15edd6e 100644 --- a/src/remote_adapter/remote_adapter_server/BOOST.cmake +++ b/src/remote_adapter/remote_adapter_server/BOOST.cmake @@ -24,7 +24,7 @@ if(QNXNTO) set(CMAKE_FIND_ROOT_PATH "${CMAKE_FIND_ROOT_PATH}" "${BOOST_ROOT}") endif() -find_package(Boost 1.68.0 COMPONENTS system filesystem) +find_package(Boost 1.68.0 COMPONENTS filesystem system) if (NOT ${Boost_FOUND}) message(STATUS "Did not find boost. Downloading and installing boost 1.68") @@ -70,8 +70,8 @@ if (NOT ${Boost_FOUND}) endif() set(BOOST_LIBRARIES - "${BOOST_LIBRARY_DIRS}/libboost_system.a" - "${BOOST_LIBRARY_DIRS}/libboost_filesystem.a") + "${BOOST_LIBRARY_DIRS}/libboost_filesystem.a" + "${BOOST_LIBRARY_DIRS}/libboost_system.a") include_directories("${BOOST_INCLUDE_DIRS}") diff --git a/src/remote_adapter/remote_adapter_server/plugins/utils/utils_manager.cc b/src/remote_adapter/remote_adapter_server/plugins/utils/utils_manager.cc index 65480d87..fa7a4ba1 100644 --- a/src/remote_adapter/remote_adapter_server/plugins/utils/utils_manager.cc +++ b/src/remote_adapter/remote_adapter_server/plugins/utils/utils_manager.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include "../../common/constants.h" #include "../../common/custom_types.h" @@ -394,11 +395,11 @@ void UtilsManager::Bind(rpc::server &server) { LOG_ERROR("{0}: {1}", __func__, error_msg::kBadTypeValue); return response_type(error_msg::kBadTypeValue, error_codes::FAILED); } - auto receive_result = UtilsManager::ExecuteCommand(bash_command); - - return receive_result; + return ('&' == bash_command.back()) ? UtilsManager::ExecuteCommandBg(bash_command): + UtilsManager::ExecuteCommand(bash_command); }); } + std::string UtilsManager::PluginName() { return "RemoteUtilsManager"; } int UtilsManager::StartApp(const std::string &app_path, const std::string &app_name) { @@ -685,6 +686,13 @@ UtilsManager::ExecuteCommand(const std::string &bash_command) { return std::make_pair(command_output, term_status); } +std::pair +UtilsManager::ExecuteCommandBg(const std::string &bash_command) { + LOG_INFO("{0}: {1}", __func__, bash_command); + system(bash_command.c_str()); + return std::make_pair("Background process", error_codes::SUCCESS); +} + std::vector UtilsManager::GetAppPids(const std::string &app_name) { struct dirent *dirent; diff --git a/src/remote_adapter/remote_adapter_server/plugins/utils/utils_manager.h b/src/remote_adapter/remote_adapter_server/plugins/utils/utils_manager.h index 73b50357..da087ac4 100644 --- a/src/remote_adapter/remote_adapter_server/plugins/utils/utils_manager.h +++ b/src/remote_adapter/remote_adapter_server/plugins/utils/utils_manager.h @@ -143,6 +143,11 @@ class UtilsManager : public remote_adapter::UtilsPlugin { static std::pair ExecuteCommand(const std::string &bash_command); + static + std::pair + ExecuteCommandBg(const std::string &bash_command); + + private: static std::vector GetAppPids(const std::string &app_name); static std::string GetAppStatus(int pid, int *num_threads = 0); diff --git a/tools/measure_sdl.sh b/tools/measure_sdl.sh new file mode 100755 index 00000000..a545521b --- /dev/null +++ b/tools/measure_sdl.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash + +# apt-get install sysstat linux-tools-common + +SDL_PID=$(pidof smartDeviceLinkCore) +REMOTE=false +OUTPUT_DIR="measure"/$1 +OUTPUT_PIDSTAT_FILE=$OUTPUT_DIR/pidstat.log +OUTPUT_PS_FILE=$OUTPUT_DIR/ps.log +OUTPUT_DOCKER_FILE=$OUTPUT_DIR/docker.log +CONTAINTER_NAME=$(docker inspect --format='{{.Name}}' $HOSTNAME | cut -c2-) + +STEP=1 + +if [ -n $2 ] && [ "$2" = "--remote" ]; then REMOTE=true; fi + +rm -rf $OUTPUT_DIR +mkdir -p $OUTPUT_DIR +pwd +echo $OUTPUT_DIR +function ps_formated() { + ps --no-headers --format "start %cpu cp cputime %mem sz thcount " -p $SDL_PID +} + +function measure_ps() { + PS_OUTPUT=$(ps_formated) + TIME=$(date +'%T:%N') + echo $TIME $PS_OUTPUT >> $OUTPUT_PS_FILE +} + +function measure_pid_stat() { + export S_TIME_FORMAT=ISO + STAT_OUTPUT=$(pidstat -urdlv -h -p $SDL_PID | grep $SDL_PID) + echo $STAT_OUTPUT >> $OUTPUT_PIDSTAT_FILE +} + +function measure_docker() { + RECORD=$(docker stats --format "{{.Name}} {{.CPUPerc}} {{.MemUsage}} {{.PIDs}}" --no-stream | grep $CONTAINTER_NAME) + echo $RECORD + TIME=$(date +'%T:%N') + echo $TIME $RECORD >> $OUTPUT_DOCKER_FILE +} + +echo "Wait for SDL start" +until SDL_PID=$(pidof smartDeviceLinkCore) +do + sleep $STEP + printf "." +done + +while SDL_PID=$(pidof smartDeviceLinkCore) +do + measure_pid_stat + measure_ps + if [ $REMOTE = true ]; then measure_docker; fi + sleep $STEP +done + +PARAMS="--pidstat_file=$OUTPUT_PIDSTAT_FILE --ps_file=$OUTPUT_PS_FILE --output_dir=$OUTPUT_DIR --title=$1" +if [ $REMOTE = true ]; then PARAMS="$PARAMS --docker_file=$OUTPUT_DOCKER_FILE"; fi + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +python3 $DIR/sdl_graphs.py $PARAMS diff --git a/tools/sdl_graphs.py b/tools/sdl_graphs.py new file mode 100755 index 00000000..526d56da --- /dev/null +++ b/tools/sdl_graphs.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python +# coding: utf-8 + +import matplotlib +matplotlib.use('Agg') +from scipy.stats import norm +from matplotlib import pyplot as plt +import matplotlib.dates as mdates +import numpy as np +from datetime import datetime +import datetime as dt +import argparse +import os +import re + + +def generator(line): + line = line.split() + for x in line: + yield x + +def sdl_start_time_ps(lines): + process_start = datetime.strptime(lines[0].split()[1], "%H:%M:%S") + return process_start + +def parse_ps(line): + # time + ps --no-headers --format "start %cpu cp cputime %mem sz thcount" + line = line.split() + record = {} + record["time"] = datetime.strptime(line[0][:-3], "%H:%M:%S:%f") + record["start"] = datetime.strptime(line[1], "%H:%M:%S") + record["pcpu"] = float(line[2]) + record["cp"] = float(line[3]) + record["cputime"] = datetime.strptime(line[4], "%H:%M:%S") + record["pmem"] = float(line[5]) + record["sz"] = float(line[6]) + record["thcount"] = int(line[7]) + return record + +def sdl_start_time_docker(lines): + line = lines[0].split() + + process_start = datetime.strptime(line[0][:-3], "%H:%M:%S:%f") + return process_start + +def parse_docker(line): + # docker stats --format "{{.Name}} {{.CPUPerc}} {{.MemUsage}} {{.PIDs}}" --no-stream + columns = generator(line) + + next_col = lambda : next(columns) + record = {} + record["time"] = datetime.strptime(next_col()[:-3], "%H:%M:%S:%f") + record["Name"] = next_col() + record["CPUPerc"] = float(next_col().replace("%", "")) + record["MemUsage"] = float(re.sub(r'[A-z]', '', next_col())) + slash = next_col() + record["MemLimit"] = next_col() + record["PIDs"] = int(next_col()) + + return record + +def graphs(record_type): + if record_type == "ps": + return ["pcpu", "pmem", "sz", "thcount"] + if record_type == "pidstat": + return ["pCPU", "VSZ", "RSS", "pMEM", "threads"] + if record_type == "docker": + return ["CPUPerc", "MemUsage", "PIDs"] + raise "Unknown record_type {}".format(record_type) + +def sdl_start_time_pidstat(lines): + process_start = datetime.strptime(lines[0].split()[0], "%H:%M:%S") + return process_start + +def parse_pidstat(line): + # pidstat -urdlv -h + columns = generator(line) + next_col = lambda : next(columns) + record = {} + record["time"] = datetime.strptime(next_col(), "%H:%M:%S") + uid = next_col() + pid = next_col() + record["pusr"] = float(next_col()) + record["psystem"] = float(next_col()) + record["pguest"] = float(next_col()) + record["pwait"] = float(next_col()) + record["pCPU"] = float(next_col()) + record["CPU"] = int(next_col()) + record["minflt/s"] = float(next_col()) + record["majflt/s"] = float(next_col()) + record["VSZ"] = int(next_col()) + record["RSS"] = int(next_col()) + record["pMEM"] = float(next_col()) + record["kB_rd/s"] = float(next_col()) + record["kB_wr/s"] = float(next_col()) + record["kB_ccwr/s"] = float(next_col()) + record["iodelay"] = float(next_col()) + record["threads"] = float(next_col()) + record["fd-nr"] = float(next_col()) + record["Command"] = next_col() + return record + +def read_records(filename, parsing_func, parsing_proc_stat): + f=open(filename) + lines = f.readlines() + process_start = parsing_proc_stat(lines) + print("Started at ", process_start) + records = [] + for line in lines: + try: + record = parsing_func(line) + delta = record["time"] - process_start + delta = datetime(1970,1,1) + delta + record["delta"] = delta + records.append(record) + except Exception as e: + print(line) + raise e + return records + +def plot(x, y ,records, title, xlable, ylable, file_pattern): + assert(len(x) == len(y)) + assert(len(x) != 0) + fig, ax = plt.subplots(figsize=(20,5)) + ax.plot(x,y) + ax.xaxis.set_major_formatter(mdates.DateFormatter('%M:%S')) + fig.autofmt_xdate() + plt.title(title) + plt.ylabel(ylable) + plt.xlabel(xlable) + plt.savefig("{}_{}.png".format(file_pattern, ylable), format='png') + # plt.show() + +def plot_record(key, records, title = None, xlable="Time", ylable=None, file_pattern = "output"): + if ylable is None: + ylable = key + if title is None: + title = key + x = [record["delta"] for record in records] + y = [record[key] for record in records] + plot(x, y, records, title, xlable, ylable, file_pattern) + +def process_records(records, record_type, title, output_dir): + if len(records) == 0: + print("empty records :".format(record_type)) + for key in graphs(record_type): + plot_record(key, records, title = title, + file_pattern = output_dir + "_" + record_type) + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--pidstat_file", help='''File with pidstat output in format : + pidstat -urdlv -h ''', + type=str) + parser.add_argument("--ps_file", help='''File with ps output in format : + time + ps --no-headers --format "start %cpu cp cputime %mem sz thcount"''', + type=str) + parser.add_argument("--docker_file", help='''File with docker stats output in format : + docker stats --format "{{.Name}} {{.CPUPerc}} {{.MemUsage}} {{.PIDs}}"''', + type=str) + parser.add_argument("--output_dir", help='''Output Directory''', + type=str) + parser.add_argument("--title", help='''Graph title''', + type=str) + args = parser.parse_args() + + if args.pidstat_file: + print("pidstat log file : {}".format(args.pidstat_file)) + records = read_records(args.pidstat_file, parse_pidstat, sdl_start_time_pidstat) + process_records(records, "pidstat", + args.title + "\npidstat -urdlv -h", + os.path.join(args.output_dir , re.sub("\D", "", args.output_dir))) + + if args.ps_file: + print("ps log file : {}".format(args.ps_file)) + records = read_records(args.ps_file, parse_ps, sdl_start_time_ps) + process_records(records, "ps", + args.title + '\nps --no-headers --format "start %cpu cp cputime %mem sz thcount"', + os.path.join(args.output_dir , re.sub("\D", "", args.output_dir))) + if args.docker_file: + print("docker log file : {}".format(args.docker_file)) + records = read_records(args.docker_file, parse_docker, sdl_start_time_docker) + process_records(records, "docker", + args.title + '\n{{.Name}} {{.CPUPerc}} {{.MemUsage}} {{.PIDs}}', + os.path.join(args.output_dir , re.sub("\D", "", args.output_dir))) + + + print("Done") + +if __name__ == "__main__": + main()