diff --git a/openmp/runtime/src/kmp.h b/openmp/runtime/src/kmp.h --- a/openmp/runtime/src/kmp.h +++ b/openmp/runtime/src/kmp.h @@ -2535,6 +2535,7 @@ kmp_int32 rec_num_taskred; } kmp_tdg_info_t; +extern int __kmp_tdg_dot; extern kmp_int32 __kmp_max_tdgs; extern kmp_tdg_info_t **__kmp_global_tdgs; extern kmp_int32 __kmp_curr_tdg_idx; diff --git a/openmp/runtime/src/kmp_global.cpp b/openmp/runtime/src/kmp_global.cpp --- a/openmp/runtime/src/kmp_global.cpp +++ b/openmp/runtime/src/kmp_global.cpp @@ -559,6 +559,7 @@ #if OMPX_TASKGRAPH // TDG record & replay +int __kmp_tdg_dot = 0; kmp_int32 __kmp_max_tdgs = 100; kmp_tdg_info_t **__kmp_global_tdgs = NULL; kmp_int32 __kmp_curr_tdg_idx = diff --git a/openmp/runtime/src/kmp_settings.cpp b/openmp/runtime/src/kmp_settings.cpp --- a/openmp/runtime/src/kmp_settings.cpp +++ b/openmp/runtime/src/kmp_settings.cpp @@ -1267,6 +1267,16 @@ void *data) { __kmp_stg_print_int(buffer, name, __kmp_max_tdgs); } // __kmp_std_print_max_tdgs + +static void __kmp_stg_parse_tdg_dot(char const *name, char const *value, + void *data) { + __kmp_stg_parse_bool(name, value, &__kmp_tdg_dot); +} // __kmp_stg_parse_tdg_dot + +static void __kmp_stg_print_tdg_dot(kmp_str_buf_t *buffer, char const *name, + void *data) { + __kmp_stg_print_bool(buffer, name, __kmp_tdg_dot); +} // __kmp_stg_print_tdg_dot #endif static void __kmp_stg_parse_num_hidden_helper_threads(char const *name, @@ -5624,6 +5634,7 @@ #if OMPX_TASKGRAPH {"KMP_MAX_TDGS", __kmp_stg_parse_max_tdgs, __kmp_std_print_max_tdgs, NULL, 0, 0}, + {"KMP_TDG_DOT", __kmp_stg_parse_tdg_dot, __kmp_stg_print_tdg_dot, NULL, 0, 0}, #endif #if OMPT_SUPPORT diff --git a/openmp/runtime/src/kmp_tasking.cpp b/openmp/runtime/src/kmp_tasking.cpp --- a/openmp/runtime/src/kmp_tasking.cpp +++ b/openmp/runtime/src/kmp_tasking.cpp @@ -5467,6 +5467,39 @@ return res; } +// __kmp_print_tdg_dot: prints the TDG to a dot file +// tdg: ID of the TDG +void __kmp_print_tdg_dot(kmp_tdg_info_t *tdg) { + kmp_int32 tdg_id = tdg->tdg_id; + KA_TRACE(10, ("__kmp_print_tdg_dot(enter): T#%d tdg_id=%d \n", gtid, tdg_id)); + + char file_name[20]; + sprintf(file_name, "tdg_%d.dot", tdg_id); + kmp_safe_raii_file_t tdg_file(file_name, "w"); + + kmp_int32 num_tasks = KMP_ATOMIC_LD_RLX(&tdg->num_tasks); + fprintf(tdg_file, + "digraph TDG {\n" + " compound=true\n" + " subgraph cluster {\n" + " label=TDG_%d\n", + tdg_id); + for (kmp_int32 i = 0; i < num_tasks; i++) { + fprintf(tdg_file, " %d[style=bold]\n", i); + } + fprintf(tdg_file, " }\n"); + for (kmp_int32 i = 0; i < num_tasks; i++) { + kmp_int32 nsuccessors = tdg->record_map[i].nsuccessors; + kmp_int32 *successors = tdg->record_map[i].successors; + if (nsuccessors > 0) { + for (kmp_int32 j = 0; j < nsuccessors; j++) + fprintf(tdg_file, " %d -> %d \n", i, successors[j]); + } + } + fprintf(tdg_file, "}"); + KA_TRACE(10, ("__kmp_print_tdg_dot(exit): T#%d tdg_id=%d \n", gtid, tdg_id)); +} + // __kmp_start_record: launch the execution of a previous // recorded TDG // gtid: Global Thread ID @@ -5636,6 +5669,9 @@ this_record_map[i].npredecessors); } KMP_ATOMIC_ST_RLX(&__kmp_tdg_task_id, 0); + + if (__kmp_tdg_dot) + __kmp_print_tdg_dot(tdg); } // __kmpc_end_record_task: wrapper around __kmp_end_record to mark diff --git a/openmp/runtime/test/tasking/omp_record_replay_print_dot.cpp b/openmp/runtime/test/tasking/omp_record_replay_print_dot.cpp new file mode 100644 --- /dev/null +++ b/openmp/runtime/test/tasking/omp_record_replay_print_dot.cpp @@ -0,0 +1,80 @@ +// REQUIRES: ompx_taskgraph +// RUN: %libomp-cxx-compile-and-run +#include +#include +#include +#include + +// Compiler-generated code (emulation) +typedef struct ident { + void* dummy; +} ident_t; + +#ifdef __cplusplus +extern "C" { + int __kmpc_global_thread_num(ident_t *); + int __kmpc_start_record_task(ident_t *, int, int, int); + void __kmpc_end_record_task(ident_t *, int, int , int); +} +#endif + +void func(int *num_exec) { + #pragma omp atomic + (*num_exec)++; +} + +std::string tdg_string= "digraph TDG {\n" +" compound=true\n" +" subgraph cluster {\n" +" label=TDG_0\n" +" 0[style=bold]\n" +" 1[style=bold]\n" +" 2[style=bold]\n" +" 3[style=bold]\n" +" }\n" +" 0 -> 1 \n" +" 1 -> 2 \n" +" 1 -> 3 \n" +"}"; + +int main() { + int num_exec = 0; + int x, y; + + setenv("KMP_TDG_DOT","TRUE",1); + remove("tdg_0.dot"); + + #pragma omp parallel + #pragma omp single + { + int gtid = __kmpc_global_thread_num(nullptr); + int res = __kmpc_start_record_task(nullptr, gtid, /* kmp_tdg_flags */ 0, /* tdg_id */ 0); + if (res) { + #pragma omp task depend(out : x) + func(&num_exec); + #pragma omp task depend(in : x) depend(out : y) + func(&num_exec); + #pragma omp task depend(in : y) + func(&num_exec); + #pragma omp task depend(in : y) + func(&num_exec); + } + + __kmpc_end_record_task(nullptr, gtid, /* kmp_tdg_flags */ 0, /* tdg_id */ 0); + } + + assert(num_exec == 4); + + std::ifstream tdg_file("tdg_0.dot"); + assert(tdg_file.is_open()); + + std::stringstream tdg_file_stream; + tdg_file_stream << tdg_file.rdbuf(); + int equal = tdg_string.compare(tdg_file_stream.str()); + + assert(equal == 0); + + std::cout << "Passed" << std::endl; + return 0; +} +// CHECK: Passed