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
81 changes: 80 additions & 1 deletion src/paralleltimertree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
#include "paralleltimertree.hpp"
#include "prettyprinttable.hpp"
#include "common.hpp"

#ifdef _OPENMP
#include "omp.h"
#endif
Expand Down Expand Up @@ -490,6 +489,79 @@ bool ParallelTimerTree::printTimers(double minFraction, const std::map<std::stri
return true;
}

/**
* @brief Generates a txt file with flame graph data.
*/
bool ParallelTimerTree::generateFlameGraphDataFile(
const std::string &filename) {
// Helper
auto join = [](const std::vector<std::string> &elements,
const std::string &delim) -> std::string {
if (elements.empty()) {
return "";
}
std::string result = elements[0];
for (std::size_t i = 1; i < elements.size(); ++i) {
result += delim + elements[i];
}
return result;
};

if (rankInPrint == 0) {
if (stats.id.empty()) {
return true;
}

std::vector<double> exclusiveTimeSum = stats.timeSum;
std::vector<int> parentIndexStack;
// Calculate exclusive timers for flamegraph by removing children
for (std::size_t i = 1; i < stats.id.size(); ++i) {
const int currentLevel = stats.level[i];
while (!parentIndexStack.empty() &&
stats.level[parentIndexStack.back()] >= currentLevel) {
parentIndexStack.pop_back();
}

if (!parentIndexStack.empty()) {
int parentIndex = parentIndexStack.back();
exclusiveTimeSum[parentIndex] -= stats.timeSum[i];
}
parentIndexStack.push_back(i);
}

std::ofstream f(filename);
if (!f.is_open()) {
std::cerr << "Error: Could not open file for writing flamegraph data: "
<< filename << std::endl;
return false;
}

std::vector<std::string> stack;
for (unsigned int i = 0; i < stats.id.size(); i++) {
const int id = stats.id[i];
const int level = stats.level[i];

if (level < 0) {
continue;
}

std::string label;
if (id != -1) {
label = (*this)[id].getLabel();
} else {
label = "Unknown element";
}

stack.resize(level);
stack.push_back(label);

auto value = 1.0e3 * exclusiveTimeSum[i];
f << join(stack, ";") << " " << value << "\n";
}
f.close();
}
return true;
}

//print out global timers
//If any labels differ, then this print will deadlock. Only call it with a communicator that is guaranteed to be consistent on all processes.
Expand Down Expand Up @@ -736,6 +808,13 @@ bool ParallelTimerTree::print(MPI_Comm communicator, std::string fileNamePrefix)
}
output.close();
}
//Generate detailed flamegraph file
std::stringstream fname_flamegraph;
fname_flamegraph << fileNamePrefix << "_flamegraph"<< ".txt";
if (!generateFlameGraphDataFile(fname_flamegraph.str())){
std::cerr << "ERROR: Failed to generate flamegraph file!" << std::endl;
}

MPI_Comm_free(&printComm);
}

Expand Down
2 changes: 2 additions & 0 deletions src/paralleltimertree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ class ParallelTimerTree: public TimerTree {
const std::map<std::string, std::string> &groupIds,
std::ofstream &output);

bool generateFlameGraphDataFile(const std::string &outputPath);

bool printGroupStatistics(double minFraction,
const std::map<std::string, std::string> &groupIds,
std::ofstream &output);
Expand Down