GCC r187297 (2012-05) introduced `__gcov_dump` and `__gcov_reset`.
`__gcov_flush = __gcov_dump + __gcov_reset`
The resolution to https://gcc.gnu.org/PR93623 ("No need to dump gcdas when forking" target GCC 11.0) removed the unuseful and undocumented __gcov_flush.
Close PR38064.
Reviewed By: calixte, serge-sans-paille
Differential Revision: https://reviews.llvm.org/D83149
// runtime's functionality.
if (hasExportSymbolDirective(Args)) {
if (ForGCOV) {
- addExportedSymbol(CmdArgs, "___gcov_flush");
+ addExportedSymbol(CmdArgs, "___gcov_dump");
+ addExportedSymbol(CmdArgs, "___gcov_reset");
addExportedSymbol(CmdArgs, "_flush_fn_list");
addExportedSymbol(CmdArgs, "_writeout_fn_list");
addExportedSymbol(CmdArgs, "_reset_fn_list");
// Check that the noredzone flag is set on the generated functions.
// CHECK: void @__llvm_gcov_writeout() unnamed_addr [[NRZ:#[0-9]+]]
-// CHECK: void @__llvm_gcov_flush() unnamed_addr [[NRZ]]
// CHECK: void @__llvm_gcov_init() unnamed_addr [[NRZ]]
// CHECK: attributes [[NRZ]] = { {{.*}}noredzone{{.*}} }
// RUN: FileCheck -check-prefix=GCOV_EXPORT %s < %t.log
// RUN: %clang -target x86_64-apple-darwin12 -fprofile-arcs -Xlinker -exported_symbols_list -Xlinker /dev/null -### %t.o 2> %t.log
// RUN: FileCheck -check-prefix=GCOV_EXPORT %s < %t.log
-// GCOV_EXPORT: "-exported_symbol" "___gcov_flush"
+// GCOV_EXPORT: "-exported_symbol" "___gcov_dump"
+// GCOV_EXPORT: "-exported_symbol" "___gcov_reset"
//
// Check that we can pass the outliner down to the linker.
// RUN: env IPHONEOS_DEPLOYMENT_TARGET=7.0 \
}
COMPILER_RT_VISIBILITY
-void llvm_register_flush_function(fn_ptr fn) {
- fn_list_insert(&flush_fn_list, fn);
-}
-
-void __gcov_flush() {
- struct fn_node* curr = flush_fn_list.head;
-
- while (curr) {
- curr->fn();
- curr = curr->next;
- }
-}
-
-COMPILER_RT_VISIBILITY
-void llvm_delete_flush_function_list(void) {
- fn_list_remove(&flush_fn_list);
-}
-
-COMPILER_RT_VISIBILITY
void llvm_register_reset_function(fn_ptr fn) {
fn_list_insert(&reset_fn_list, fn);
}
#endif
COMPILER_RT_VISIBILITY
-void llvm_gcov_init(fn_ptr wfn, fn_ptr ffn, fn_ptr rfn) {
+void llvm_gcov_init(fn_ptr wfn, fn_ptr rfn) {
static int atexit_ran = 0;
if (wfn)
llvm_register_writeout_function(wfn);
- if (ffn)
- llvm_register_flush_function(ffn);
-
if (rfn)
llvm_register_reset_function(rfn);
/* Make sure we write out the data and delete the data structures. */
atexit(llvm_delete_reset_function_list);
- atexit(llvm_delete_flush_function_list);
#ifdef _WIN32
atexit(llvm_writeout_and_clear);
#endif
}
}
+void __gcov_dump(void) {
+ for (struct fn_node *f = writeout_fn_list.head; f; f = f->next)
+ f->fn();
+}
+
+void __gcov_reset(void) {
+ for (struct fn_node *f = reset_fn_list.head; f; f = f->next)
+ f->fn();
+}
+
#endif
#endif
dlerror();
- void (*gcov_flush1)() = (void (*)())dlsym(f1_handle, "__gcov_flush");
- if (gcov_flush1 == NULL) {
- fprintf(stderr, "unable to find __gcov_flush in func.shared': %s\n", dlerror());
+ void (*gcov_reset1)() = (void (*)())dlsym(f1_handle, "__gcov_reset");
+ if (gcov_reset1 == NULL) {
+ fprintf(stderr, "unable to find __gcov_reset in func.shared': %s\n", dlerror());
return EXIT_FAILURE;
}
dlerror();
- void (*gcov_flush2)() = (void (*)())dlsym(f2_handle, "__gcov_flush");
- if (gcov_flush2 == NULL) {
- fprintf(stderr, "unable to find __gcov_flush in func2.shared': %s\n", dlerror());
+ void (*gcov_reset2)() = (void (*)())dlsym(f2_handle, "__gcov_reset");
+ if (gcov_reset2 == NULL) {
+ fprintf(stderr, "unable to find __gcov_reset in func2.shared': %s\n", dlerror());
return EXIT_FAILURE;
}
- if (gcov_flush1 == gcov_flush2) {
- fprintf(stderr, "Same __gcov_flush found in func.shared and func2.shared\n");
+ if (gcov_reset1 == gcov_reset2) {
+ fprintf(stderr, "Same __gcov_reset found in func.shared and func2.shared\n");
return EXIT_FAILURE;
}
func3();
#endif
- void (*gcov_flush1)() = (void (*)())dlsym(f1_handle, "__gcov_flush");
- if (gcov_flush1 == NULL)
- return fprintf(stderr, "unable to find __gcov_flush in func1.so': %s\n", dlerror());
- void (*gcov_flush2)() = (void (*)())dlsym(f2_handle, "__gcov_flush");
- if (gcov_flush2 == NULL)
- return fprintf(stderr, "unable to find __gcov_flush in func2.so': %s\n", dlerror());
- if (gcov_flush1 == gcov_flush2)
- return fprintf(stderr, "same __gcov_flush found in func1.so and func2.so\n");
+ void (*gcov_reset1)() = (void (*)())dlsym(f1_handle, "__gcov_reset");
+ if (gcov_reset1 == NULL)
+ return fprintf(stderr, "unable to find __gcov_reset in func1.so': %s\n", dlerror());
+ void (*gcov_reset2)() = (void (*)())dlsym(f2_handle, "__gcov_reset");
+ if (gcov_reset2 == NULL)
+ return fprintf(stderr, "unable to find __gcov_reset in func2.so': %s\n", dlerror());
+ if (gcov_reset1 == gcov_reset2)
+ return fprintf(stderr, "same __gcov_reset found in func1.so and func2.so\n");
+
+ /// Test that __gcov_dump is in the dynamic symbol table.
+ void (*gcov_dump1)() = (void (*)())dlsym(f1_handle, "__gcov_dump");
+ if (gcov_dump1 == NULL)
+ return fprintf(stderr, "unable to find __gcov_dump in func1.so': %s\n", dlerror());
if (dlclose(f2_handle) != 0)
return fprintf(stderr, "unable to close 'func2.so': %s\n", dlerror());
// RUN: %clang --coverage -fPIC -shared shared.c -o libfunc.so
// RUN: test -f shared.gcno
-/// Test the case where we exit abruptly after calling __gcov_flush, which means we don't write out the counters at exit.
+/// Test the case where we exit abruptly after calling __gcov_dump, which means we don't write out the counters at exit.
// RUN: %clang -DEXIT_ABRUPTLY -DSHARED_CALL_BEFORE_FLUSH -DSHARED_CALL_AFTER_FLUSH --coverage %s -L%t.d -rpath %t.d -lfunc -o %t
// RUN: test -f gcov-shared-flush.gcno
// SHARED: 1: {{[[0-9]+}}:void foo(int n)
-/// Test the case where we exit normally and we have a call to the shared library function before __gcov_flush.
+/// Test the case where we exit normally and we have a call to the shared library function before __gcov_dump.
// RUN: %clang -DSHARED_CALL_BEFORE_FLUSH --coverage %s -L%t.d -rpath %t.d -lfunc -o %t
// RUN: test -f gcov-shared-flush.gcno
// BEFORE: -: {{[0-9]+}}:#ifdef SHARED_CALL_BEFORE_FLUSH
// BEFORE-NEXT: 1: {{[0-9]+}}: foo(1);
-// BEFORE: 1: {{[0-9]+}}: __gcov_flush();
+// BEFORE: 1: {{[0-9]+}}: __gcov_dump();
+// BEFORE-NEXT: 1: {{[0-9]+}}: __gcov_reset();
// BEFORE: -: {{[0-9]+}}:#ifdef SHARED_CALL_AFTER_FLUSH
// BEFORE-NEXT: -: {{[0-9]+}}: foo(1);
// BEFORE: 1: {{[0-9]+}}: bar(5);
// SHARED_ONCE: 1: {{[0-9]+}}:void foo(int n)
-// # Test the case where we exit normally and we have a call to the shared library function after __gcov_flush.
+// # Test the case where we exit normally and we have a call to the shared library function after __gcov_dump.
// RUN: %clang -DSHARED_CALL_AFTER_FLUSH --coverage %s -L%t.d -rpath %t.d -lfunc -o %t
// RUN: test -f gcov-shared-flush.gcno
// AFTER: -: {{[0-9]+}}:#ifdef SHARED_CALL_BEFORE_FLUSH
// AFTER-NEXT: -: {{[0-9]+}}: foo(1);
-// AFTER: 1: {{[0-9]+}}: __gcov_flush();
+// AFTER: 1: {{[0-9]+}}: __gcov_dump();
+// AFTER-NEXT: 1: {{[0-9]+}}: __gcov_reset();
// AFTER: -: {{[0-9]+}}:#ifdef SHARED_CALL_AFTER_FLUSH
// AFTER-NEXT: 1: {{[0-9]+}}: foo(1);
// AFTER: 1: {{[0-9]+}}: bar(5);
-// # Test the case where we exit normally and we have calls to the shared library function before and after __gcov_flush.
+// # Test the case where we exit normally and we have calls to the shared library function before and after __gcov_dump.
// RUN: %clang -DSHARED_CALL_BEFORE_FLUSH -DSHARED_CALL_AFTER_FLUSH --coverage %s -L%t.d -rpath %t.d -lfunc -o %t
// RUN: test -f gcov-shared-flush.gcno
// BEFORE_AFTER: -: {{[0-9]+}}:#ifdef SHARED_CALL_BEFORE_FLUSH
// BEFORE_AFTER-NEXT: 1: {{[0-9]+}}: foo(1);
-// BEFORE_AFTER: 1: {{[0-9]+}}: __gcov_flush();
+// BEFORE_AFTER: 1: {{[0-9]+}}: __gcov_dump();
+// BEFORE_AFTER-NEXT: 1: {{[0-9]+}}: __gcov_reset();
// BEFORE_AFTER: -: {{[0-9]+}}:#ifdef SHARED_CALL_AFTER_FLUSH
// BEFORE_AFTER-NEXT: 1: {{[0-9]+}}: foo(1);
// BEFORE_AFTER: 1: {{[0-9]+}}: bar(5);
}
#else
extern void foo(int n);
-extern void __gcov_flush(void);
+extern void __gcov_dump(void);
+extern void __gcov_reset(void);
int bar1 = 0;
int bar2 = 1;
#endif
bar(5);
- __gcov_flush();
+ __gcov_dump();
+ __gcov_reset();
bar(5);
#ifdef SHARED_CALL_AFTER_FLUSH
// CHECK: -: 0:Runs:1
// CHECK-NEXT: -: 0:Programs:1
-void __gcov_flush(void);
+void __gcov_dump(void);
+void __gcov_reset(void);
int main(void) { // CHECK: 1: [[#@LINE]]:int main(void)
int i = 22; // CHECK-NEXT: 1: [[#@LINE]]:
- __gcov_flush(); // CHECK-NEXT: 1: [[#@LINE]]:
+ __gcov_dump(); // CHECK-NEXT: 1: [[#@LINE]]:
+ __gcov_reset(); // CHECK-NEXT: 1: [[#@LINE]]:
i = 42; // CHECK-NEXT: 1: [[#@LINE]]:
__builtin_trap(); // CHECK-NEXT: 1: [[#@LINE]]:
i = 84; // CHECK-NEXT: 1: [[#@LINE]]:
// RUN: rm -f gcov-dump-and-remove.gcda && %run %t
// RUN: llvm-cov gcov -t gcov-dump-and-remove.gcda | FileCheck %s
-extern void __gcov_flush(void);
+extern void __gcov_dump(void);
+extern void __gcov_reset(void);
extern int remove(const char *); // CHECK: -: [[#@LINE]]:extern int remove
int main(void) { // CHECK-NEXT: #####: [[#@LINE]]:
- __gcov_flush(); // CHECK-NEXT: #####: [[#@LINE]]:
+ __gcov_dump(); // CHECK-NEXT: #####: [[#@LINE]]:
+ __gcov_reset(); // CHECK-NEXT: #####: [[#@LINE]]:
if (remove("gcov-dump-and-remove.gcda") != 0) // CHECK-NEXT: #####: [[#@LINE]]:
return 1; // CHECK-NEXT: #####: [[#@LINE]]: return 1;
// CHECK-NEXT: -: [[#@LINE]]:
- __gcov_flush(); // CHECK-NEXT: #####: [[#@LINE]]:
- __gcov_flush(); // CHECK-NEXT: #####: [[#@LINE]]:
- if (remove("gcov-dump-and-remove.gcda") != 0) // CHECK-NEXT: #####: [[#@LINE]]:
+ __gcov_dump(); // CHECK-NEXT: 1: [[#@LINE]]:
+ __gcov_reset(); // CHECK-NEXT: 1: [[#@LINE]]:
+ __gcov_dump(); // CHECK-NEXT: 1: [[#@LINE]]:
+ if (remove("gcov-dump-and-remove.gcda") != 0) // CHECK-NEXT: 1: [[#@LINE]]:
return 1; // CHECK-NEXT: #####: [[#@LINE]]: return 1;
return 0;
Function *
insertCounterWriteout(ArrayRef<std::pair<GlobalVariable *, MDNode *>>);
Function *insertReset(ArrayRef<std::pair<GlobalVariable *, MDNode *>>);
- Function *insertFlush(Function *ResetF);
bool AddFlushBeforeForkAndExec();
Function *WriteoutF = insertCounterWriteout(CountersBySP);
Function *ResetF = insertReset(CountersBySP);
- Function *FlushF = insertFlush(ResetF);
// Create a small bit of code that registers the "__llvm_gcov_writeout" to
// be executed at exit and the "__llvm_gcov_flush" function to be executed
IRBuilder<> Builder(BB);
FTy = FunctionType::get(Type::getVoidTy(*Ctx), false);
- Type *Params[] = {PointerType::get(FTy, 0), PointerType::get(FTy, 0),
- PointerType::get(FTy, 0)};
- FTy = FunctionType::get(Builder.getVoidTy(), Params, false);
+ auto *PFTy = PointerType::get(FTy, 0);
+ FTy = FunctionType::get(Builder.getVoidTy(), {PFTy, PFTy}, false);
// Initialize the environment and register the local writeout, flush and
// reset functions.
FunctionCallee GCOVInit = M->getOrInsertFunction("llvm_gcov_init", FTy);
- Builder.CreateCall(GCOVInit, {WriteoutF, FlushF, ResetF});
+ Builder.CreateCall(GCOVInit, {WriteoutF, ResetF});
Builder.CreateRetVoid();
appendToGlobalCtors(*M, F, 0);
return ResetF;
}
-
-Function *GCOVProfiler::insertFlush(Function *ResetF) {
- FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), false);
- Function *FlushF = M->getFunction("__llvm_gcov_flush");
- if (!FlushF)
- FlushF = Function::Create(FTy, GlobalValue::InternalLinkage,
- "__llvm_gcov_flush", M);
- FlushF->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
- FlushF->addFnAttr(Attribute::NoInline);
- if (Options.NoRedZone)
- FlushF->addFnAttr(Attribute::NoRedZone);
-
- BasicBlock *Entry = BasicBlock::Create(*Ctx, "entry", FlushF);
-
- // Write out the current counters.
- Function *WriteoutF = M->getFunction("__llvm_gcov_writeout");
- assert(WriteoutF && "Need to create the writeout function first!");
-
- IRBuilder<> Builder(Entry);
- Builder.CreateCall(WriteoutF, {});
- Builder.CreateCall(ResetF, {});
-
- Type *RetTy = FlushF->getReturnType();
- if (RetTy->isVoidTy())
- Builder.CreateRetVoid();
- else if (RetTy->isIntegerTy())
- // Used if __llvm_gcov_flush was implicitly declared.
- Builder.CreateRet(ConstantInt::get(RetTy, 0));
- else
- report_fatal_error("invalid return type for __llvm_gcov_flush");
-
- return FlushF;
-}