Skip to content

Commit

Permalink
Merge pull request #7 from pfpsim/tracing
Browse files Browse the repository at this point in the history
Add support for live tracing counters, latency and throughput
  • Loading branch information
grodtron committed Aug 23, 2016
2 parents 0da6537 + 08514b9 commit 531084c
Show file tree
Hide file tree
Showing 12 changed files with 380 additions and 25 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ Makefile
*Targets.cmake
_CPack_Packages/
.deb
doc/
doc/pfp-p4/pfp-p4-prefix/
pfpsim/core/config.h
pfp-p4/pfp-p4-prefix/
3 changes: 2 additions & 1 deletion .travis/run_lint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
# +y --> don't ignore category y
# Separate each by a comma with no space
# Example: -whitespace,+whitespace/braces --> ignores all 'whitespace' errors except the 'whitespace/braces' errors.
filters="-runtime/references,-build/include_subdir,-build/c++11"
# TODO(gordon) Technically we should not need to remove 'build/include' but in v1.3.0 of cpplint it seems to cause false-positives
filters="-runtime/references,-build/include_subdir,-build/include,-build/c++11"

# Root Directory: Used to determine appropriate header guards
# The path must be relative to the location of the directory containing the .git file
Expand Down
25 changes: 24 additions & 1 deletion pfpsim/core/PFPObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ PFPObject::PFPObject(const std::string& module_name,
GlobalConfigPath(CONFIGROOT),
dicp_enabled(enable_dicp),
module_name_(module_name),
fully_qualified_module_name_(""), // Built and set lazily
parent_(parent) {
// cout<<module_name_<<" BaseConfigFile is:"<<BaseConfigFile<<endl;
// cout<<module_name_<<" InstanceConfigFile is:"<<InstanceConfigFile<<endl;
Expand Down Expand Up @@ -149,12 +150,34 @@ const std::string& PFPObject::module_name() const {
return module_name_;
}

const std::string& PFPObject::fully_qualified_module_name() const {
if (fully_qualified_module_name_ == "") {
std::stringstream ss;
bool first = true;
auto hierarchy = ModuleHierarchy();
auto it = hierarchy.rbegin();
auto end = hierarchy.rend();
++it; // skip 'top'
for (; it != end; ++it) {
if (!first) {
ss << '.';
} else {
first = false;
}
ss << *it;
}
fully_qualified_module_name_ = ss.str();
}

return fully_qualified_module_name_;
}

PFPObject* PFPObject::GetParent() {
return parent_;
}


std::vector<std::string> PFPObject::ModuleHierarchy() {
std::vector<std::string> PFPObject::ModuleHierarchy() const {
std::vector<std::string> hierarchy;
hierarchy.push_back(module_name());
std::string name = module_name();
Expand Down
10 changes: 6 additions & 4 deletions pfpsim/core/PFPObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,13 @@ class PFPObject {
pfp::core::ConfigurationParameterNode
GetParameterfromParent(std::string param, PFPObject* parent);

std::vector<std::string> ModuleHierarchy();
std::vector<std::string> ModuleHierarchy() const;
/* ----- DICP ----- */
const bool dicp_enabled;

/* --- Module ---- */
const std::string& module_name() const;
const std::string& fully_qualified_module_name() const;
PFPObject* GetParent();

/* --- Observers --- */
Expand Down Expand Up @@ -223,7 +224,7 @@ class PFPObject {
double sim_time) {
for (auto& each_observer : observers_) {
auto func = std::bind(&PFPObserver::data_written, each_observer,
module_name(), data, sim_time);
fully_qualified_module_name(), data, sim_time);
events_.push(func);
}
}
Expand All @@ -237,7 +238,7 @@ class PFPObject {
double sim_time) {
for (auto& each_observer : observers_) {
auto func = std::bind(&PFPObserver::data_read, each_observer,
module_name(), data, sim_time);
fully_qualified_module_name(), data, sim_time);
events_.push(func);
}
}
Expand All @@ -251,7 +252,7 @@ class PFPObject {
std::string& drop_reason, double sim_time) {
for (auto& each_observer : observers_) {
auto func = std::bind(&PFPObserver::data_dropped, each_observer,
module_name(), data, drop_reason, sim_time);
fully_qualified_module_name(), data, drop_reason, sim_time);
events_.push(func);
}
}
Expand All @@ -270,6 +271,7 @@ class PFPObject {
protected:
const std::string GlobalConfigPath;
const std::string module_name_;
mutable std::string fully_qualified_module_name_;
PFPObject* parent_; /*!< Parent of this PFPObject */
std::map<std::string, std::string> configMap; /*!< Configuration Map used >*/
//! Store counters and values
Expand Down
129 changes: 124 additions & 5 deletions pfpsim/core/debugger/DebugDataManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ namespace core {
namespace db {

DebugDataManager::DebugDataManager()
: simulation_time(0.0),
: trace_id(0),
simulation_time(0.0),
current_packet_id(-1),
next_watchpoint_id(0),
break_packet_dropped(false) {}
Expand All @@ -55,6 +56,59 @@ void DebugDataManager::removeCounter(std::string name) {
counters.erase(name);
}

int DebugDataManager::addCounterTrace(const std::string & name) {
std::lock_guard<std::mutex> guard(mutex_);

// If there's no counter with this name, then we can't set a trace
if (counters.find(name) == counters.end()) {
return -1;
}

// If a trace already exists, we won't make a new one.
if (counter_traces.find(name) != counter_traces.end()) {
return -2;
}

// Creating the trace itself is really as simple as assigning it
// an ID.
int id = trace_id++;

counter_traces[name] = id;

return id;
}

int DebugDataManager::addLatencyTrace(const std::string & from_module_name,
const std::string & to_module_name) {
std::lock_guard<std::mutex> guard(mutex_);

int id = trace_id++;

latency_read_triggers[from_module_name].push_back({id, to_module_name});

return id;
}

int DebugDataManager::addThroughputTrace(const std::string & module_name) {
std::lock_guard<std::mutex> guard(mutex_);

int id = trace_id++;

throughput_triggers[module_name] = {id, -1};

return id;
}

int DebugDataManager::getCounterTraceId(const std::string & name) {
auto it = counter_traces.find(name);

if (it == counter_traces.end()) {
return -1;
} else {
return it->second;
}
}

void DebugDataManager::updateCounter(std::string name, int val) {
std::lock_guard<std::mutex> guard(mutex_);
auto it = counters.find(name);
Expand Down Expand Up @@ -127,10 +181,13 @@ void DebugDataManager::disableBreakpoint(int id) {
}
}

void DebugDataManager::updatePacket(int id,
std::shared_ptr<const DebugInfo> di,
std::string module, double time_,
bool read) {


std::vector<DebugDataManager::TraceData>
DebugDataManager::updatePacket(int id,
std::shared_ptr<const DebugInfo> di,
std::string module, double time_,
bool read) {
std::lock_guard<std::mutex> guard(mutex_);

auto check_pk = packet_list.find(id);
Expand All @@ -147,8 +204,70 @@ void DebugDataManager::updatePacket(int id,
packet.setTime(time_);
packet.setCurrentLocation(module);
packet.updateTraceReadTime(module, time_);

auto it = latency_read_triggers.find(module);
if (it != latency_read_triggers.end()) {
// For each latency trace triggered by this read:
for (auto & trigger : it->second) {
// We store a trigger for the write-module of that latency trace,
// associated with the packet id, and the trace id and read timestamp.
// Multiple latency traces can end at the same module, which is why we
// push back instead of just setting a single entry
latency_write_triggers[trigger.write_module_name][id].push_back(
{trigger.trace_id, time_});
}
}
// No new trace updates;
return {};

} else {
packet.updateTraceWriteTime(module, time_);

std::vector<DebugDataManager::TraceData> trace_updates;

// Latency Updates
// TODO(gordon) factor out into function
{
auto it = latency_write_triggers.find(module);
if (it != latency_write_triggers.end()) {
// And if any of those correspond to this particular packet
auto pack_it = it->second.find(id);
if (pack_it != it->second.end()) {
// Then for each of those triggers
for (auto & trigger : pack_it->second) {
// We update it's associated trace with the difference between the
// read time and the write time.
trace_updates.push_back({trigger.trace_id,
time_ - trigger.read_time});
}
// Then we delete the set of triggers for this packet ID
it->second.erase(pack_it);
// We don't bother deleting the outer map entry, because if writes
// to this module have been part of any trace, they likely will be
// again, and there's no point in deleting the map if it's likely
// just going to be recreated again. On the other hand, once we've
// seen a particular packet id, we don't expect to ever see it again.
}
}
}

// Throughput updates
// TODO(gordon) factor out into function
{
auto it = throughput_triggers.find(module);
if (it != throughput_triggers.end()) {
double & last_time = it->second.last_time;
if (last_time >= 0) {
// Divide time delta by 1e9 to get time in seconds, then take the
// reciprocal to get packets per second
trace_updates.push_back({it->second.trace_id,
(1000*1000*1000)/(time_ - last_time)});
}
last_time = time_;
}
}

return trace_updates;
}
}

Expand Down
66 changes: 63 additions & 3 deletions pfpsim/core/debugger/DebugDataManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,30 @@ class DebugDataManager {
*/
void addBreakpoint(Breakpoint br);

/**
* Add a trace to a counter by name
* @param name The name of the counter to trace
* @return The ID of the trace.
*/
int addCounterTrace(const std::string & name);

/**
* Add a trace of the latency from ingress of one module to
* egress of another.
* @param from_module_name The name of the ingress module
* @param to_module_name The name of the egress module
* @return The ID of the newly created trace.
*/
int addLatencyTrace(const std::string & from_module_name,
const std::string & to_module_name);

/**
* Add a trace of the throughput of a module
* @param module_name The name of the module to trace.
* @return The ID of the newly created trace.
*/
int addThroughputTrace(const std::string & module_name);

/**
* Remove a Breakpoint.
* @param identifier ID of Breakpoint to remove.
Expand All @@ -107,16 +131,25 @@ class DebugDataManager {
*/
void disableBreakpoint(int id);

struct TraceData {
int id;
double value;
};

/**
* Add a new packet or update an existing one.
* @param id ID of packet.
* @param di The DebugObject representing this packet.
* @param module Name of module the packet is currently in.
* @param time_ Time of update.
* @param read Indicates whether the update is for a read or a write. True = read, False = write.
* @param read Indicates whether the update is for a read or a write.
* True = read, False = write.
*/
void updatePacket(int id, std::shared_ptr<const DebugInfo> di,
std::string module, double time_, bool read);
std::vector<TraceData> updatePacket(int id,
std::shared_ptr<const DebugInfo> di,
std::string module,
double time_,
bool read);

/**
* Remove a packet.
Expand Down Expand Up @@ -276,9 +309,36 @@ class DebugDataManager {
*/
void setSimulationTime(double time_ns);

int getCounterTraceId(const std::string & name);

private:
//! Map with counter name as key and counter value as the value.
std::map<std::string, int> counters;
//! The next id to be assigned to a trace.
int trace_id;
//! Map of counters we're tracing, and their trace IDs.
std::map<std::string, int> counter_traces;

struct LatencyReadTrigger {
int trace_id;
std::string write_module_name;
};
std::map<std::string, std::vector<LatencyReadTrigger>> latency_read_triggers;

struct LatencyWriteTrigger {
int trace_id;
double read_time;
};
std::map<std::string,
std::map<int,
std::vector<LatencyWriteTrigger>>> latency_write_triggers;

struct ThroughputTrigger {
int trace_id;
double last_time;
};
std::map<std::string, ThroughputTrigger> throughput_triggers;

//! Mutex to make sure only one thread access the variables
//! of this class at a time.
std::mutex mutex_;
Expand Down

0 comments on commit 531084c

Please sign in to comment.