Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Contribute DiagnosticCountInPassHook #4629

Merged
merged 8 commits into from
Apr 25, 2024
Merged
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
7 changes: 7 additions & 0 deletions backends/p4test/p4test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ limitations under the License.
#include "frontends/p4/toP4/toP4.h"
#include "ir/ir.h"
#include "ir/json_loader.h"
#include "ir/pass_utils.h"
#include "lib/crash.h"
#include "lib/error.h"
#include "lib/exceptions.h"
Expand Down Expand Up @@ -127,16 +128,22 @@ int main(int argc, char *const argv[]) {
error(ErrorType::ERR_IO, "Can't open %s", options.file);
}
} else {
P4::DiagnosticCountInfo info;
program = P4::parseP4File(options);
info.emitInfo("PARSER");

if (program != nullptr && ::errorCount() == 0) {
P4::P4COptionPragmaParser optionsPragmaParser;
program->apply(P4::ApplyOptionsPragmas(optionsPragmaParser));
info.emitInfo("PASS P4COptionPragmaParser");

if (!options.parseOnly) {
try {
P4::FrontEnd fe;
fe.addDebugHook(hook);
// use -TdiagnosticCountInPass:1 / -TdiagnosticCountInPass:4 to get output of
// this hook
fe.addDebugHook(info.getPassManagerHook());
program = fe.run(options, program);
} catch (const std::exception &bug) {
std::cerr << bug.what() << std::endl;
Expand Down
2 changes: 2 additions & 0 deletions ir/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ set (IR_SRCS
json_parser.cpp
node.cpp
pass_manager.cpp
pass_utils.cpp
type.cpp
v1.cpp
visitor.cpp
Expand All @@ -50,6 +51,7 @@ set (IR_HDRS
node.h
nodemap.h
pass_manager.h
pass_utils.h
vector.h
visitor.h
)
Expand Down
106 changes: 106 additions & 0 deletions ir/pass_utils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
Copyright 2024 Intel Corp.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "ir/pass_utils.h"

#include <initializer_list>
#include <tuple>

#include "absl/strings/str_cat.h"
#include "lib/log.h"
#include "pass_utils.h"

/// @file
/// @brief Utilities for passes and pass managers.

namespace P4 {

// The state needs to be shared between all hook copies as the hook is copied in the pass manager.
struct DiagnosticCountInfoState {
explicit DiagnosticCountInfoState(BaseCompileContext &ctxt)
: ctxt(ctxt),
lastDiagCount(ctxt.errorReporter().getDiagnosticCount()),
lastErrorCount(ctxt.errorReporter().getErrorCount()),
lastWarningCount(ctxt.errorReporter().getWarningCount()),
lastInfoCount(ctxt.errorReporter().getInfoCount()) {}

void info(std::string_view componentInfo) {
if (!Log::fileLogLevelIsAtLeast(DIAGNOSTIC_COUNT_IN_PASS_TAG, 1)) return;

unsigned diag = ctxt.errorReporter().getDiagnosticCount();

if (diag != lastDiagCount) {
unsigned errs = ctxt.errorReporter().getErrorCount(),
warns = ctxt.errorReporter().getWarningCount(),
infos = ctxt.errorReporter().getInfoCount();
std::stringstream summary;
const char *sep = "";
for (auto [newCnt, oldCnt, kind] : {std::tuple(errs, lastErrorCount, "error"),
std::tuple(warns, lastWarningCount, "warning"),
std::tuple(infos, lastInfoCount, "info")}) {
if (newCnt > oldCnt) {
auto diff = newCnt - oldCnt;
summary << sep << diff << " " << kind << (diff > 1 ? "s" : "");
sep = ", ";
}
}

LOG_FEATURE(DIAGNOSTIC_COUNT_IN_PASS_TAG, 1,
componentInfo << " emitted " << summary.str());
lastDiagCount = diag;
lastErrorCount = errs;
lastWarningCount = warns;
lastInfoCount = infos;
}
}

BaseCompileContext &ctxt;
unsigned lastDiagCount, lastErrorCount, lastWarningCount, lastInfoCount;
};

static DebugHook hook(std::shared_ptr<DiagnosticCountInfoState> state) {
return [state](const char *manager, unsigned seqNo, const char *pass, const IR::Node *) {
if (!Log::fileLogLevelIsAtLeast(DIAGNOSTIC_COUNT_IN_PASS_TAG, 1)) return;

std::string compInfo = absl::StrCat("PASS ", manager);
// If the log level is high enough, emit also pass count.
if (Log::fileLogLevelIsAtLeast(DIAGNOSTIC_COUNT_IN_PASS_TAG, 4)) {
absl::StrAppend(&compInfo, "[", seqNo, "]");
}
absl::StrAppend(&compInfo, " ~> ", pass);

state->info(compInfo);
};
}

DebugHook getDiagnosticCountInPassHook(BaseCompileContext &ctxt) {
return hook(std::make_shared<DiagnosticCountInfoState>(ctxt));
}

DiagnosticCountInfoGuard::~DiagnosticCountInfoGuard() { state->info(componentInfo); }

DiagnosticCountInfo::DiagnosticCountInfo(BaseCompileContext &ctxt)
: state(std::make_shared<DiagnosticCountInfoState>(ctxt)) {}

DebugHook DiagnosticCountInfo::getPassManagerHook() { return hook(state); }

void DiagnosticCountInfo::emitInfo(std::string_view componentInfo) { state->info(componentInfo); }

DiagnosticCountInfoGuard DiagnosticCountInfo::getInfoGuard(std::string_view componentInfo) {
return {componentInfo, state};
}

} // namespace P4
100 changes: 100 additions & 0 deletions ir/pass_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
Copyright 2024 Intel Corp.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#ifndef IR_PASS_UTILS_H_
#define IR_PASS_UTILS_H_

#include "ir/pass_manager.h"
#include "lib/compile_context.h"

/// @file
vlstill marked this conversation as resolved.
Show resolved Hide resolved
/// @brief Utilities for passes and pass managers.

namespace P4 {

inline const char *DIAGNOSTIC_COUNT_IN_PASS_TAG = "diagnosticCountInPass";

/// @brief This creates a debug hook that prints information about the number of diagnostics of
/// different levels (error, warning, info) printed by each of the pass after it ended. This is
/// intended to help in debuggig and testing.
///
/// NOTE: You either need to use one \ref DiagnosticCountInfo object (and its \ref
/// getPassManagerHook) for the entire compilation, create one pass and use its copies in all the
/// pass managers in the compilation, or create each of the following hooks only after the previous
/// compilations steps had already run. Otherwise, you can get spurious logs for a first pass of a
/// pass manager running after some diagnostics were emitted.
///
/// It logs the messages if the log level for "diagnosticCountInPass" is at least 1. If the log
/// level is at least 4, the logs also include the sequence number of the pass that printed the
/// message in the pass manager.
///
/// @param ctxt Optionally, you can provide a compilation context to take the diagnostic counts
/// from. If not provied BaseCompileContext::get() is used.
/// @return A debug hook suitable for using in pass managers.
DebugHook getDiagnosticCountInPassHook(BaseCompileContext &ctxt = BaseCompileContext::get());

struct DiagnosticCountInfoState; // forward declaration

/// A guard useful for emitting diagnostic info about non-passes.
/// \see DiagnosticCountInfo::getInfoGuard.
struct DiagnosticCountInfoGuard {
~DiagnosticCountInfoGuard();

/// avoid creating moved-from object and copies that could emit spurious logs.
DiagnosticCountInfoGuard(DiagnosticCountInfoGuard &&) = delete;

friend struct DiagnosticCountInfo;

private:
DiagnosticCountInfoGuard(std::string_view componentInfo,
std::shared_ptr<DiagnosticCountInfoState> state)
: componentInfo(componentInfo), state(state) {}
std::string_view componentInfo;
std::shared_ptr<DiagnosticCountInfoState> state;
};

/// An alternative interface to the diagnostic count info that allows using it outside of pass
/// manager (but can also generate a pass manager hook).
/// All copies of one object and all hooks and guards derived from it share the same diagnostic
/// message counts so they can provide consistent logs.
///
/// \see getDiagnosticCountInPassHook
struct DiagnosticCountInfo {
/// @param ctxt Optionally, you can provide a compilation context to take the diagnostic counts
/// from. If not provied BaseCompileContext::get() is used.
explicit DiagnosticCountInfo(BaseCompileContext &ctxt = BaseCompileContext::get());

/// Very similar to \ref getDiagnosticCountInPassHook, but the state is tied with this instance
/// so that calling the hook and the \ref emitInfo/\ref getInfoGuard during one compilation will
/// produce the correct counts.
DebugHook getPassManagerHook();

/// Emits the information like printed by the debug hook, except using componentInfo as the info
/// at the beginning of the line: the line will be in form "<componentInfo> emitted <counts>".
/// This is useful e.g. for printing info about diagnostics in parser.
void emitInfo(std::string_view componentInfo);

/// Similar to \ref emitInfo, but prints the info at the moment the returned guard is
/// destructed.
DiagnosticCountInfoGuard getInfoGuard(std::string_view componentInfo);

private:
std::shared_ptr<DiagnosticCountInfoState> state;
};

} // namespace P4

#endif // IR_PASS_UTILS_H_
Loading