// Instrumentation based profiling warnings.
def ProfileInstrOutOfDate : DiagGroup<"profile-instr-out-of-date">;
+def ProfileInstrUnprofiled : DiagGroup<"profile-instr-unprofiled">;
"profile data may be out of date: of %0 function%s0, %1 %plural{1:has|:have}1"
" no data and %2 %plural{1:has|:have}2 mismatched data that will be ignored">,
InGroup<ProfileInstrOutOfDate>;
+def warn_profile_data_unprofiled : Warning<
+ "no profile data available for file \"%0\"">,
+ InGroup<ProfileInstrUnprofiled>;
+
} // end of instrumentation issue category
} // end of sema component.
DeferredDeclsToEmit.clear();
}
+void InstrProfStats::reportDiagnostics(DiagnosticsEngine &Diags,
+ StringRef MainFile) {
+ if (!hasDiagnostics())
+ return;
+ if (VisitedInMainFile > 0 && VisitedInMainFile == MissingInMainFile) {
+ if (MainFile.empty())
+ MainFile = "<stdin>";
+ Diags.Report(diag::warn_profile_data_unprofiled) << MainFile;
+ } else
+ Diags.Report(diag::warn_profile_data_out_of_date) << Visited << Missing
+ << Mismatched;
+}
+
void CodeGenModule::Release() {
EmitDeferred();
applyReplacements();
if (getCodeGenOpts().ProfileInstrGenerate)
if (llvm::Function *PGOInit = CodeGenPGO::emitInitialization(*this))
AddGlobalCtor(PGOInit, 0);
- if (PGOReader && PGOStats.isOutOfDate())
- getDiags().Report(diag::warn_profile_data_out_of_date)
- << PGOStats.Visited << PGOStats.Missing << PGOStats.Mismatched;
+ if (PGOReader && PGOStats.hasDiagnostics())
+ PGOStats.reportDiagnostics(getDiags(), getCodeGenOpts().MainFileName);
EmitCtorList(GlobalCtors, "llvm.global_ctors");
EmitCtorList(GlobalDtors, "llvm.global_dtors");
EmitGlobalAnnotations();
};
/// This class records statistics on instrumentation based profiling.
-struct InstrProfStats {
- InstrProfStats() : Visited(0), Missing(0), Mismatched(0) {}
- bool isOutOfDate() { return Missing || Mismatched; }
+class InstrProfStats {
+ uint32_t VisitedInMainFile;
+ uint32_t MissingInMainFile;
uint32_t Visited;
uint32_t Missing;
uint32_t Mismatched;
+
+public:
+ InstrProfStats()
+ : VisitedInMainFile(0), MissingInMainFile(0), Visited(0), Missing(0),
+ Mismatched(0) {}
+ /// Record that we've visited a function and whether or not that function was
+ /// in the main source file.
+ void addVisited(bool MainFile) {
+ if (MainFile)
+ ++VisitedInMainFile;
+ ++Visited;
+ }
+ /// Record that a function we've visited has no profile data.
+ void addMissing(bool MainFile) {
+ if (MainFile)
+ ++MissingInMainFile;
+ ++Missing;
+ }
+ /// Record that a function we've visited has mismatched profile data.
+ void addMismatched(bool MainFile) { ++Mismatched; }
+ /// Whether or not the stats we've gathered indicate any potential problems.
+ bool hasDiagnostics() { return Missing || Mismatched; }
+ /// Report potential problems we've found to \c Diags.
+ void reportDiagnostics(DiagnosticsEngine &Diags, StringRef MainFile);
};
/// This class organizes the cross-function state that is used while generating
emitCounterVariables();
}
if (PGOReader) {
- loadRegionCounts(PGOReader);
+ SourceManager &SM = CGM.getContext().getSourceManager();
+ loadRegionCounts(PGOReader, SM.isInMainFile(D->getLocation()));
computeRegionCounts(D);
applyFunctionAttributes(PGOReader, Fn);
}
Builder.CreateStore(Count, Addr);
}
-void CodeGenPGO::loadRegionCounts(llvm::IndexedInstrProfReader *PGOReader) {
- CGM.getPGOStats().Visited++;
+void CodeGenPGO::loadRegionCounts(llvm::IndexedInstrProfReader *PGOReader,
+ bool IsInMainFile) {
+ CGM.getPGOStats().addVisited(IsInMainFile);
RegionCounts.reset(new std::vector<uint64_t>);
uint64_t Hash;
if (PGOReader->getFunctionCounts(getFuncName(), Hash, *RegionCounts)) {
- CGM.getPGOStats().Missing++;
+ CGM.getPGOStats().addMissing(IsInMainFile);
RegionCounts.reset();
} else if (Hash != FunctionHash ||
RegionCounts->size() != NumRegionCounters) {
- CGM.getPGOStats().Mismatched++;
+ CGM.getPGOStats().addMismatched(IsInMainFile);
RegionCounts.reset();
}
}
void computeRegionCounts(const Decl *D);
void applyFunctionAttributes(llvm::IndexedInstrProfReader *PGOReader,
llvm::Function *Fn);
- void loadRegionCounts(llvm::IndexedInstrProfReader *PGOReader);
+ void loadRegionCounts(llvm::IndexedInstrProfReader *PGOReader,
+ bool IsInMainFile);
void emitCounterVariables();
llvm::GlobalVariable *buildDataVar();
--- /dev/null
+function_in_header
+10
+2
+1
+0
+
+main
+0
+1
+1
--- /dev/null
+void function_in_header(int i) {
+ if (i) {}
+}
--- /dev/null
+// Test that unprofiled files are recognized. Here, we have two functions in the
+// profile, main() and function_in_header, but we use the profile on a file that
+// has the profile-less some_unprofiled_function so that the only profiled code
+// in #included in a header.
+
+// FIXME: It would be nice to use -verify here instead of FileCheck, but -verify
+// doesn't play well with warnings that have no line number.
+
+// RUN: llvm-profdata merge %S/Inputs/c-unprofiled.proftext -o %t.profdata
+// RUN: %clang_cc1 -triple x86_64-apple-macosx10.9 -main-file-name c-unprofiled.c -I %S/Inputs/ %s -o /dev/null -emit-llvm -fprofile-instr-use=%t.profdata -Wprofile-instr-unprofiled 2>&1 | FileCheck %s
+
+// CHECK: warning: no profile data available for file "c-unprofiled.c"
+
+#include "profiled_header.h"
+
+#ifdef GENERATE_OUTDATED_DATA
+int main(int argc, const char *argv[]) {
+ function_in_header(0);
+ return 0;
+}
+#else
+void some_unprofiled_function(int i) {
+ if (i)
+ function_in_header(i);
+}
+#endif