diff --git a/Makefile b/Makefile index 861608c..80e92dd 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,9 @@ all: g++ -std=c++14 gtop.cc utils.cc display.cc -o gtop -pedantic -Wall -Wextra -lncurses -lpthread +clean: + rm gtop + fake: g++ -std=c++14 gtop.cc utils.cc display.cc -o gtop -pedantic -Wall -Wextra -lncurses -lpthread -DTEGRASTATS_FAKE diff --git a/display.cc b/display.cc index a3da1fe..61a8130 100644 --- a/display.cc +++ b/display.cc @@ -121,6 +121,11 @@ void display_cpu_stats(const int & row, const tegrastats & ts) { } } +void display_emc_stats(const int & row, const tegrastats & ts) { + mvprintw(row, 0, "EMC"); + display_bars(row, BAR_OFFSET, ts.emc_usage, ts.emc_freq); +} + void display_gpu_stats(const int & row, const tegrastats & ts) { mvprintw(row, 0, "GPU"); display_bars(row, BAR_OFFSET, ts.gpu_usage, ts.gpu_freq); diff --git a/display.hh b/display.hh index 9c68a8d..ed6d2f7 100644 --- a/display.hh +++ b/display.hh @@ -69,6 +69,7 @@ void clear_row(const int &, const int &); void display_cpu_stats(const int &, const tegrastats &); void display_gpu_stats(const int &, const tegrastats &); void display_mem_stats(const int &, const tegrastats &); +void display_emc_stats(const int &, const tegrastats &); void display_usage_chart(const int &, const std::vector>); #endif // DISPLAY_HH_ diff --git a/gtop.cc b/gtop.cc index d511ebc..f89a5e9 100644 --- a/gtop.cc +++ b/gtop.cc @@ -1,22 +1,96 @@ // Martin Kersner, m.kersner@gmail.com // 2017/04/21 +// Modified by Zach LaCelle, zlacelle@gmail.com +// 2018/07/23 #include "gtop.hh" +#include +#include +#include std::mutex m; std::condition_variable cv; tegrastats t_stats; +std::string JETSON_TYPE="N/A"; +bool WRITE_CSV=false; +FILE* csvFile=nullptr; bool processed = false; bool ready = false; bool finished = false; -int main() { +int main(int argc, char** argv) { if (getuid()) { std::cout << "gtop requires root privileges!" << std::endl; exit(1); } + //Get options + const char* short_opts = "21kc"; + static struct option long_options[] = + { + {"tx2", no_argument, nullptr, '2'}, + {"tx1", no_argument, nullptr, '1'}, + {"tk1", no_argument, nullptr, 'k'}, + {"csv", no_argument, nullptr, 'c'}, + {nullptr, no_argument, nullptr, 0} + }; + + while( 1 ) + { + const auto opt = getopt_long(argc, argv, short_opts, long_options, nullptr); + + if( opt == -1 ) + { + break; + } + + switch(opt) + { + case '2': + { + JETSON_TYPE="TX2"; + break; + } + case '1': + { + JETSON_TYPE="TX1"; + break; + } + case 'k': + { + JETSON_TYPE="TK1"; + break; + } + case 'c': + { + WRITE_CSV=true; + break; + } + default: + { + printf("ERROR: Bad Argument.\n"); + exit(-1); + } + } + } //end: while( 1 ) + + if( JETSON_TYPE.compare("N/A") == 0 ) + { + printf("ERROR: Must have Jetson board type option (--tx2, --tx1, --tk1)\n"); + exit(-1); + } + + if( WRITE_CSV ) + { + csvFile = fopen("gtop.csv", "w"); + if( csvFile == nullptr ) + { + printf("ERROR: Could not open gtop.csv for writing.\n"); + exit(-1); + } + } + std::thread t(read_tegrastats); initscr(); @@ -53,7 +127,12 @@ int main() { update_usage_chart(cpu_usage_buffer, t_stats.cpu_usage); display_usage_chart(10, cpu_usage_buffer); - + // Write to CSV, if desired + if( WRITE_CSV ) + { + write_csv(t_stats); + } + lk.unlock(); refresh(); @@ -104,33 +183,59 @@ void read_tegrastats() { cv.wait(lk, []{ return ready; }); ready = false; - t_stats = parse_tegrastats(buffer.data()); + //Maybe in the future, we want to only continue if we get enough stats + bool foundMem = false; + bool foundCPU = false; + bool foundEMC = false; + bool foundGPU = false; + t_stats = parse_tegrastats(&foundMem, &foundCPU, &foundEMC, &foundGPU, buffer.data()); processed = true; + + static bool needCsvHeader = true; + if( needCsvHeader == true && WRITE_CSV == true ) + { + int numCpus = t_stats.cpu_usage.size(); + write_csv_header(numCpus); + needCsvHeader = false; + } + lk.unlock(); cv.notify_one(); } } } -tegrastats parse_tegrastats(const char * buffer) { +tegrastats parse_tegrastats(bool* _foundMem, + bool* _foundCPU, + bool* _foundEMC, + bool* _foundGPU, + const char * _buffer) +{ tegrastats ts; - auto stats = tokenize(buffer, ' '); - - if (stats.size() >= 15) + auto stats = tokenize(_buffer, ' '); + + if( JETSON_TYPE.compare("TX2") == 0 ) + { + ts.version = TX2; + } + else if( JETSON_TYPE.compare("TX1") == 0 ) + { ts.version = TX1; + } else - ts.version = TX2; - - get_mem_stats(ts, stats.at(1)); + { + ts.version = TK1; + } switch (ts.version) { case TX1: + get_mem_stats(ts, stats.at(1)); + //get_tx1_stats(ts, stats); get_cpu_stats_tx1(ts, stats.at(5)); get_gpu_stats(ts, stats.at(15)); break; case TX2: - get_cpu_stats_tx2(ts, stats.at(5)); - get_gpu_stats(ts, stats.at(13)); + get_tx2_stats(ts, _foundMem, _foundCPU, _foundEMC, _foundGPU, stats); break; case TK1: // TODO break; @@ -139,6 +244,69 @@ tegrastats parse_tegrastats(const char * buffer) { return ts; } +void get_tx2_stats(tegrastats & ts, + bool* _foundMem, + bool* _foundCPU, + bool* _foundEMC, + bool* _foundGPU, + const std::vector _line) +{ + + for(auto iter = _line.begin(); iter != _line.end(); ++iter) + { + std::string tokstr = *iter; + if( tokstr.compare("RAM") == 0 ) + { + ++iter; + if( iter != _line.end() ) + { + std::string memstr = *iter; + get_mem_stats(ts, memstr); + *_foundMem = true; + } + } + else if( tokstr.compare("CPU") == 0 ) + { + ++iter; + if( iter != _line.end() ) + { + std::string cpustr = *iter; + get_cpu_stats_tx2(ts, cpustr); + *_foundCPU = true; + } + } + else if( tokstr.compare("EMC_FREQ") == 0 ) + { + ++iter; + if( iter != _line.end() ) + { + std::string emcstr = *iter; + get_emc_stats(ts, emcstr); + *_foundEMC = true; + } + } + else if( tokstr.compare("GR3D_FREQ") == 0 ) + { + ++iter; + if( iter != _line.end() ) + { + std::string gpustr = *iter; + get_gpu_stats(ts, gpustr); + *_foundGPU = true; + } + } + } //end: for( ... ) +} + +void get_emc_stats(tegrastats & ts, const std::string & str) +{ + auto emc_stats = tokenize(str, '@'); + auto emc_usage = emc_stats.at(0); + + ts.emc_usage = std::stoi(emc_usage.substr(0, emc_usage.size()-1)); + ts.emc_freq = std::stoi(emc_stats.at(1)); +} + void get_cpu_stats_tx1(tegrastats & ts, const std::string & str) { auto cpu_stats = tokenize(str, '@'); auto cpu_usage_all = cpu_stats.at(0); @@ -192,12 +360,15 @@ void get_mem_stats(tegrastats & ts, const std::string & str) { void display_stats(const dimensions & d, const tegrastats & ts) { // CPU display_cpu_stats(0, ts); - + // GPU display_gpu_stats(ts.cpu_usage.size(), ts); // Memory - display_mem_stats(ts.cpu_usage.size()+1, ts); + display_mem_stats(1 + ts.cpu_usage.size(), ts); + + // EMC + display_emc_stats(2 + ts.cpu_usage.size(), ts); } void update_usage_chart(std::vector> & usage_buffer, @@ -210,3 +381,42 @@ void update_usage_chart(std::vector> & usage_buffer, usage_buffer.push_back(usage); } + +void write_csv(tegrastats & ts) +{ + char utc[128]; + time_t rawtime; + struct tm* timeinfo; + time(&rawtime); + timeinfo = localtime(&rawtime); + strftime(utc,128,"%Y/%m/%d %T",timeinfo); + puts(utc); + struct timespec timesp; + clock_gettime(CLOCK_MONOTONIC, ×p); + float monotime = timesp.tv_sec + (timesp.tv_nsec / 1e9); + fprintf(csvFile, "%s,%f,%d,%d,%d,%d,%d,%d",utc,monotime,ts.mem_usage,ts.mem_max,ts.gpu_usage,ts.gpu_freq,ts.emc_usage,ts.emc_freq); + int num_cpus = ts.cpu_usage.size(); + for(int i=0; i); void get_cpu_stats_tx1(tegrastats &, const std::string &); void get_cpu_stats_tx2(tegrastats &, const std::string &); void get_gpu_stats(tegrastats &, const std::string &); void get_mem_stats(tegrastats &, const std::string &); +void get_emc_stats(tegrastats &, const std::string &); void display_stats(const dimensions &, const tegrastats &); void update_usage_chart(std::vector> &, const std::vector &); +void write_csv(tegrastats &); +void write_csv_header(int numCpus); + #endif // GTOP_HH_ diff --git a/utils.hh b/utils.hh index 71e7522..8b87b7c 100644 --- a/utils.hh +++ b/utils.hh @@ -22,6 +22,9 @@ struct tegrastats { int gpu_usage; int gpu_freq; + int emc_usage; + int emc_freq; + jetson_version version; };