Skip to content
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
84 changes: 84 additions & 0 deletions profiler/native/inc/thread_tracker.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
////////////////////////////////////////////////////////////////////////////////
// Module: thread_tracker.hpp
//
////////////////////////////////////////////////////////////////////////////////

#ifndef __THREAD_TRACKER_HPP__
#define __THREAD_TRACKER_HPP__

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

#include <vector>
#include <unordered_map>
#include <string>
#include <map>
#include <queue>
#include <utility>
#include <time.h>
#include <set>

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
typedef std::unordered_map<int, std::unordered_map<std::string, std::map<time_t, time_t> > > thread_information_t;

namespace ev31 {

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

struct thread_snapshot
{
public:
thread_snapshot();
thread_snapshot(time_t start_time);
~thread_snapshot();

public:
void add_involved_thread(int thread_id, const std::string& method_name);
std::set<std::pair<int, std::string> > involved_threads;
time_t start_time;
time_t end_time;
};



class thread_tracker
{
// Ctor / Dtor
public:
thread_tracker(int snapshot_lookback);
thread_tracker();
~thread_tracker();

// Member Methods
public:
const thread_snapshot dump_thread_snapshot();
void save_thread_data(int thread_id, const std::string& method_name, time_t start_time);
const thread_information_t get_thread_information();
std::map<time_t, time_t> get_thread_intervals(int thread_id, const std::string& method_name, time_t interval_start, time_t interval_end);


private:
thread_information_t thread_information;
std::queue<thread_snapshot> snapshots;
int snapshot_lookback;

void start_snapshot();
const thread_snapshot get_latest_snapshot();
void write_to_thread_information(thread_information_t &thread_information, int thread_id, const std::string& method_name, time_t start_time);
void write_to_thread_information(thread_information_t &thread_information, int thread_id, const std::string& method_name, time_t start_time, time_t end_time);

};
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

} // end of namespace (ev31)

// ////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////

#endif // __THREAD_TRACKER_HPP__

// ////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////
140 changes: 140 additions & 0 deletions profiler/native/src/thread_tracker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
////////////////////////////////////////////////////////////////////////////////
// Module: thread_tracker.cpp
//
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

#include "../inc/thread_tracker.hpp"
#include <algorithm>
#include <chrono>

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// thread_snapshot
////////////////////////////////////////////////////////////////////////////////
ev31::thread_snapshot::thread_snapshot()
{
this->start_time = time(NULL);
this->end_time = time(NULL);
}

ev31::thread_snapshot::thread_snapshot(time_t start_time)
{
this->involved_threads = std::set<std::pair<int, std::string> >();
this->start_time = start_time;
this->end_time = time(NULL);
}

ev31::thread_snapshot::~thread_snapshot()
{

}

void ev31::thread_snapshot::add_involved_thread(int thread_id, const std::string& method_name)
{
involved_threads.insert(std::make_pair(thread_id, method_name));
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////



////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

// thread_tracker

////////////////////////////////////////////////////////////////////////////////
// Ctor / Dtor
////////////////////////////////////////////////////////////////////////////////
ev31::thread_tracker::thread_tracker() : thread_tracker(10)
{

}
ev31::thread_tracker::thread_tracker(int snapshot_lookback)
{
this->snapshot_lookback = snapshot_lookback;
this->snapshots = std::queue<thread_snapshot>();
this->start_snapshot();
}

ev31::thread_tracker::~thread_tracker()
{

}

void ev31::thread_tracker::start_snapshot()
{
if (this->snapshots.size() == snapshot_lookback)
{
snapshots.pop();
}
snapshots.push(thread_snapshot());
}

const ev31::thread_snapshot ev31::thread_tracker::dump_thread_snapshot()
{
time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
snapshots.back().end_time = now;
const ev31::thread_snapshot snapshot = snapshots.back();
start_snapshot();
return snapshot;
}

const ev31::thread_snapshot ev31::thread_tracker::get_latest_snapshot()
{
return snapshots.back();
}

void ev31::thread_tracker::save_thread_data(int thread_id, const std::string& method_name, time_t start_time)
{
write_to_thread_information(thread_information, thread_id, method_name, start_time);
snapshots.back().add_involved_thread(thread_id, method_name);
}

const thread_information_t ev31::thread_tracker::get_thread_information()
{
return thread_information;
}

void ev31::thread_tracker::write_to_thread_information(thread_information_t &thread_information, int thread_id, const std::string& method_name, time_t start_time, time_t end_time)
{
if (thread_information.find(thread_id) == thread_information.end())
{
thread_information[thread_id] = std::unordered_map<std::string, std::map<time_t, time_t>>();
if (thread_information.at(thread_id).find(method_name) == thread_information.at(thread_id).end())
{
thread_information[thread_id][method_name] = std::map<time_t, time_t>();
}
}
thread_information[thread_id][method_name][start_time] = end_time;
}

void ev31::thread_tracker::write_to_thread_information(thread_information_t &thread_information, int thread_id, const std::string& method_name, time_t start_time)
{
write_to_thread_information(thread_information, thread_id, method_name, start_time, time(NULL));
}

// Implicit assumption that the thread_id and method_name are valid
std::map<time_t, time_t> ev31::thread_tracker::get_thread_intervals(int thread_id, const std::string &method_name, time_t interval_start, time_t interval_end)
{
auto time_range_intersection = [&](const std::pair<time_t, time_t>& thread_interval) -> bool
{
return (thread_interval.second == time(nullptr)) || (thread_interval.first <= interval_end && thread_interval.second > interval_start);
};

std::map<time_t, time_t> intervals;
std::map<time_t, time_t> &map = this->thread_information[thread_id][method_name];
std::copy_if(map.begin(), map.end(), std::inserter(intervals, intervals.begin()), time_range_intersection);
return intervals;
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
43 changes: 43 additions & 0 deletions profiler/tests/hotpath/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// function example
#include <iostream>
#include <chrono>
#include <vector>
#include "../../native/inc/thread_tracker.hpp"

int main ()
{
ev31::thread_tracker tracker = ev31::thread_tracker();
std::vector<ev31::thread_snapshot> snapshots;

auto hot_path_start_time = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 10000; i++)
{
std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());

for (int j = 0; j < 1000; j++)
{
int thread_id = i * 10000 + j;
tracker.save_thread_data(thread_id, std::to_string(thread_id), now);
if (j % 100 == 0)
{
snapshots.push_back(tracker.dump_thread_snapshot());
}
}
}
auto hot_path_end_time = std::chrono::high_resolution_clock::now();

std::cout << "Hot path took " << std::chrono::duration_cast<std::chrono::milliseconds>(hot_path_end_time - hot_path_start_time).count() << " milliseconds" << std::endl;

auto snapshots_start_time = std::chrono::high_resolution_clock::now();

for (auto &snapshot : snapshots)
{
auto start = snapshot.involved_threads.begin();
tracker.get_thread_intervals(start->first, start->second, snapshot.start_time, snapshot.end_time);
}

auto snapshots_end_time = std::chrono::high_resolution_clock::now();
std::cout << "Snapshots length: " << snapshots.size() << std::endl;
std::cout << "Snapshots took " << std::chrono::duration_cast<std::chrono::milliseconds>(snapshots_end_time - snapshots_start_time).count() << " milliseconds" << std::endl;
return 0;
}