Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More robust parsing method, and adding CSV writing support #6

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -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

Expand Down
5 changes: 5 additions & 0 deletions display.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions display.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::vector<int>>);

#endif // DISPLAY_HH_
238 changes: 224 additions & 14 deletions gtop.cc
Original file line number Diff line number Diff line change
@@ -1,22 +1,96 @@
// Martin Kersner, [email protected]
// 2017/04/21
// Modified by Zach LaCelle, [email protected]
// 2018/07/23

#include "gtop.hh"
#include <getopt.h>
#include <string>
#include <time.h>

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();
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;
Expand All @@ -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<std::string> _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);
Expand Down Expand Up @@ -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<std::vector<int>> & usage_buffer,
Expand All @@ -210,3 +381,42 @@ void update_usage_chart(std::vector<std::vector<int>> & 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, &timesp);
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<num_cpus; ++i)
{
fprintf(csvFile, ",%d,%d", ts.cpu_usage[i], ts.cpu_freq[i]);
}
fprintf(csvFile, "\n");
fflush(csvFile);
}

void write_csv_header(int numCpus)
{
std::ostringstream ss;
ss << "UTC,Mono Time";
ss << ",Mem Usage,Mem Max";
ss << ",GPU Usage,GPU Freq";
ss << ",EMC Usage,EMC Freq";
for(int i=0;i<numCpus;++i)
{
ss << ",CPU " << i << " Usage,CPU " << i << " Freq";
}
ss << std::endl;
std::string headerStr = ss.str();
fprintf(csvFile, "%s", headerStr.c_str());
fflush(csvFile);
}
11 changes: 9 additions & 2 deletions gtop.hh
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,27 @@
#include "display.hh"
#include "utils.hh"

const int STATS_BUFFER_SIZE = 256;
const int STATS_BUFFER_SIZE = 1024;

const std::string TEGRASTATS_PATH = "~/tegrastats";
const std::string TEGRASTATSFAKE_PATH = "./tegrastats_fake";

void read_tegrastats();
tegrastats parse_tegrastats(const char *);
tegrastats parse_tegrastats(bool*, bool*, bool*, bool*, const char *);

void get_tx2_stats(tegrastats &,
bool*, bool*, bool*, bool*, const std::vector<std::string>);

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<std::vector<int>> &, const std::vector<int> &);

void write_csv(tegrastats &);
void write_csv_header(int numCpus);

#endif // GTOP_HH_
3 changes: 3 additions & 0 deletions utils.hh
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ struct tegrastats {
int gpu_usage;
int gpu_freq;

int emc_usage;
int emc_freq;

jetson_version version;
};

Expand Down