Skip to content

Commit

Permalink
Modified TraceFunction object to require that an environment variable…
Browse files Browse the repository at this point in the history
… be set to enable output. This makes it possible to leave the TraceFunction calls in while developing, but still be able to run and pass testing. Update TraceFunction for better formatting.
  • Loading branch information
feldergast committed Jul 16, 2024
1 parent 45f5ee8 commit 5d51df4
Show file tree
Hide file tree
Showing 10 changed files with 534 additions and 39 deletions.
172 changes: 145 additions & 27 deletions src/sst/core/output.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <atomic>
#include <cerrno>
#include <cinttypes>
#include <cstdlib>
#include <cstring>
#include <string>

Expand Down Expand Up @@ -523,64 +524,181 @@ thread_local std::vector<char> TraceFunction::indent_array(100, ' ');
thread_local int TraceFunction::trace_level = 0;

TraceFunction::TraceFunction(uint32_t line, const char* file, const char* func, bool print_sim_info, bool activate) :
line(line),
file(file),
function(func),
indent_length(2),
active(activate)
line_(line),
file_(file),
function_(func),
indent_length_(2),
active_(activate && global_active_)
{
if ( !active ) return;
if ( !active_ ) return;
if ( print_sim_info ) {
RankInfo ri = Simulation_impl::getSimulation()->getNumRanks();
if ( ri.rank > 1 || ri.thread > 1 ) { output_obj.init("@R, @I (@t): " /*prefix*/, 0, 0, Output::STDOUT); }
Simulation_impl* sim = nullptr;
try {
sim = Simulation_impl::getSimulation();
}
catch ( std::out_of_range& e ) {
// do nothing
}

if ( sim ) {
RankInfo ri = sim->getNumRanks();
if ( ri.rank > 1 || ri.thread > 1 ) { output_obj_.init("@x (@t): " /*prefix*/, 0, 0, Output::STDOUT); }
else {
output_obj_.init("(@t): ", 0, 0, Output::STDOUT);
}
}
else {
output_obj.init("(@t): ", 0, 0, Output::STDOUT);
output_obj_.init("" /*prefix*/, 0, 0, Output::STDOUT);
}
// rank = Simulation_impl::getSimulation()->getRank().rank;
// thread = Simulation_impl::getSimulation()->getRank().thread;
}
else {
output_obj.init("" /*prefix*/, 0, 0, Output::STDOUT);
output_obj_.init("" /*prefix*/, 0, 0, Output::STDOUT);
}

// Set up the indent
int indent = trace_level * indent_length;
int indent = trace_level * indent_length_;
indent_array[indent] = '\0';
output_obj.output(line, file, func, "%s%s enter function\n", indent_array.data(), function.c_str());
indent_array[indent] = ' ';
output_obj_.output(line_, file, func, "%s%s enter function\n", indent_array.data(), function_.c_str());
indent_array[indent] = indent_marker_;
fflush(stdout);
trace_level++;
}

TraceFunction::~TraceFunction()
{
if ( !active ) return;
if ( !active_ ) return;
trace_level--;
int indent = trace_level * indent_length;
int indent = trace_level * indent_length_;
indent_array[indent] = '\0';
output_obj.output(
line, file.c_str(), function.c_str(), "%s%s exit function\n", indent_array.data(), function.c_str());
output_obj_.output(
line_, file_.c_str(), function_.c_str(), "%s%s exit function\n", indent_array.data(), function_.c_str());
indent_array[indent] = ' ';
fflush(stdout);
}

void
TraceFunction::output(const char* format, ...) const
{
if ( !active ) return;
if ( !active_ ) return;
// Need to add the indent
char buf[200];
char* buf = new char[200];

int indent = trace_level * indent_length;
int indent = trace_level * indent_length_;
indent_array[indent] = '\0';
snprintf(buf, 200, "%s%s", indent_array.data(), format);
std::string message(indent_array.data());

va_list args;
va_start(args, format);
size_t n = vsnprintf(buf, 200, format, args);
va_end(args);

if ( n >= 200 ) {
// Generated string longer than buffer
delete[] buf;
buf = new char[n + 1];
va_start(args, format);
vsnprintf(buf, n + 1, format, args);
va_end(args);
}

// Look for \n and print each line individually. We do this so we
// can put the correct indent in and so that the prefix prints
// correctly.
size_t start_index = 0;
for ( size_t i = 0; i < n - 1; ++i ) {
if ( buf[i] == '\n' ) {
// Terminate string here, then change it back after printing
buf[i] = '\0';
output_obj_.outputprintf(
line_, file_.c_str(), function_.c_str(), "%s%s\n", indent_array.data(), &buf[start_index]);
buf[i] = '\n';
start_index = i + 1;
}
}

// Print the rest of the string
output_obj_.outputprintf(line_, file_.c_str(), function_.c_str(), "%s%s", indent_array.data(), &buf[start_index]);

delete[] buf;

indent_array[indent] = ' ';

va_list arg;
va_start(arg, format);
output_obj.outputprintf(line, file.c_str(), function.c_str(), buf, arg);
va_end(arg);
// output_obj_.outputprintf(line_, file_.c_str(), function_.c_str(), "%s", message.c_str());
// Since this class is for debug, force a flush after every output
fflush(stdout);
}

// void
// TraceFunction::output(const char* format, ...) const
// {
// if ( !active_ ) return;
// // Need to add the indent
// char* buf = new char[200];

// int indent = trace_level * indent_length_;
// indent_array[indent] = '\0';
// std::string message(indent_array.data());

// va_list args;
// va_start(args, format);
// size_t n = vsnprintf(buf, 200, format, args);
// va_end(args);

// if ( n >= 200 ) {
// // Generated string longer than buffer
// delete[] buf;
// buf = new char[n + 1];
// va_start(args, format);
// vsnprintf(buf, n+1, format, args);
// va_end(args);
// }

// // Replace all \n's with \n + indent_array to indent any new lines
// // in the string (unless the \n is the last character in the
// // string)
// size_t start_index = 0;
// for ( size_t i = 0; i < n - 1; ++i ) {
// if ( buf[i] == '\n' ) {
// message.append(&buf[start_index], i - start_index + 1);
// message.append(indent_array.data());
// start_index = i + 1;
// }
// }
// message.append(&buf[start_index], n - start_index);
// delete[] buf;

// indent_array[indent] = ' ';

// output_obj_.outputprintf(line_, file_.c_str(), function_.c_str(), "%s", message.c_str());
// fflush(stdout);
// }


// Functions to check for proper environment variable to turn on output
// for TraceFunction
bool
is_trace_function_active()
{
const char* var = getenv("SST_TRACEFUNCTION_ACTIVATE");
if ( var ) { return true; }
else {
return false;
}
}

char
get_indent_marker()
{
const char* var = getenv("SST_TRACEFUNCTION_INDENT_MARKER");
if ( var ) {
if ( strlen(var) > 0 )
return var[0];
else
return '|';
}
return ' ';
}

bool TraceFunction::global_active_ = is_trace_function_active();
char TraceFunction::indent_marker_ = get_indent_marker();
} // namespace SST
64 changes: 52 additions & 12 deletions src/sst/core/output.h
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,24 @@ class Output
uint32_t line, const std::string& file, const std::string& func, const char* format, va_list arg) const;
void outputprintf(const char* format, va_list arg) const;

// Versions of outputprintf that takes variable arguments instead of va_list
inline void
outputprintf(uint32_t line, const std::string& file, const std::string& func, const char* format, ...) const
{
va_list args;
va_start(args, format);
outputprintf(line, file, func, format, args);
va_end(args);
}

inline void outputprintf(const char* format, ...) const
{
va_list(args);
va_start(args, format);
outputprintf(format, args);
va_end(args);
}

friend int ::main(int argc, char** argv);
static Output& setDefaultObject(
const std::string& prefix, uint32_t verbose_level, uint32_t verbose_mask, output_location_t location,
Expand Down Expand Up @@ -555,34 +573,56 @@ class Output
static int m_mpiRank;
};

// Class to easily trace function enter and exit
/**
Class to easily trace function enter and exit. This class is for
temporary use during debugging only and is not part of the SST Core
stable API (i.e. the class can change at any time).
NOTE: Output for TraceFunction will only be turned on if the
SST_TRACEFUNCTION_ACTIVIATE envirnoment variable is set.
You can also control whether or not an "indent marker" will be used
by setting SST_TRACEFUNCTION_INDENT_MARKER. If the environment
variable is defined but empty, a | will be used. If the variable
isn't empty, it will use the first character as the indent marker.
This will look like:
static void class1::func1() enter function
| static void class1::func2() enter function
| static void class1::func2() exit function
static void class1::func1() exit function
static void class1::func1() enter function
| static void class1::func2() enter function
| static void class1::func2() exit function
static void class1::func1() exit function
*/
class TraceFunction
{

thread_local static int trace_level;
thread_local static std::vector<char> indent_array;

public:
TraceFunction(uint32_t line, const char* file, const char* func, bool print_sim_info = true, bool activate = true);
~TraceFunction();

Output& getOutput() { return output_obj; }

/** Output the message with formatting as specified by the format parameter.
@param format Format string. All valid formats for printf are available.
@param ... Arguments for format.
*/
void output(const char* format, ...) const __attribute__((format(printf, 2, 3)));

private:
Output output_obj;
uint32_t line;
std::string file;
std::string function;
// uint32_t rank;
// uint32_t thread;
int indent_length;
bool active;
Output output_obj_;
uint32_t line_;
std::string file_;
std::string function_;
int indent_length_;
bool active_;

// Static to determine if TraceFunction should be active
static bool global_active_;
static char indent_marker_;
};

} // namespace SST
Expand Down
2 changes: 2 additions & 0 deletions src/sst/core/testElements/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ libcoreTestElement_la_SOURCES = \
testElements/coreTest_Message.h \
testElements/coreTest_MessageGeneratorComponent.h \
testElements/coreTest_MessageGeneratorComponent.cc \
testElements/coreTest_Output.h \
testElements/coreTest_Output.cc \
testElements/coreTest_Serialization.h \
testElements/coreTest_Serialization.cc \
testElements/coreTest_SharedObjectComponent.h \
Expand Down
62 changes: 62 additions & 0 deletions src/sst/core/testElements/coreTest_Output.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright 2009-2024 NTESS. Under the terms
// of Contract DE-NA0003525 with NTESS, the U.S.
// Government retains certain rights in this software.
//
// Copyright (c) 2009-2024, NTESS
// All rights reserved.
//
// This file is part of the SST software package. For license
// information, see the LICENSE file in the top level directory of the
// distribution.

//#include <assert.h>

#include "sst_config.h"

#include "sst/core/testElements/coreTest_Output.h"

namespace SST {
namespace CoreTestSerialization {


void
testTraceFunction(int level = 0)
{
TraceFunction trace(CALL_INFO_LONG);
trace.output("level = %d\n", level);

if ( level == 0 ) {
// for first level, output a string long enough that it goes
// into the overflow case for the string generation in
// TraceFunction::output(). The current overflow length is 200.
int chars_in_line = 0;
std::string str;
for ( int i = 0; i < 250; ++i ) {
str += std::to_string(i % 10);
chars_in_line++;
if ( chars_in_line == 40 ) {
chars_in_line = 0;
str += '\n';
}
}
trace.output("%s\n", str.c_str());
testTraceFunction(level + 1);
}
else if ( level == 1 || level == 2 ) {
testTraceFunction(level + 1);
}
}

coreTestOutput::coreTestOutput(ComponentId_t id, Params& params) : Component(id)
{
Output& out = getSimulationOutput();

std::string test = params.find<std::string>("test");
if ( test == "" ) out.fatal(CALL_INFO_LONG, 1, "ERROR: Must specify test type\n");

if ( test == "TraceFunction" ) { testTraceFunction(); }
}


} // namespace CoreTestSerialization
} // namespace SST
Loading

0 comments on commit 5d51df4

Please sign in to comment.