-
Notifications
You must be signed in to change notification settings - Fork 448
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* P4Fmt reference checker Signed-off-by: Nitish <[email protected]> * add: sample & ref p4 files Signed-off-by: Nitish <[email protected]> * restructure p4fmt to a reusable library Signed-off-by: Nitish <[email protected]> * add missing files Signed-off-by: Nitish <[email protected]> * update header guard Signed-off-by: Nitish <[email protected]> * replace `std::cerr` with ::error(...) Signed-off-by: Nitish <[email protected]> * update README with `checkfmt` Signed-off-by: Nitish <[email protected]> * move descriptive comment to the header file Signed-off-by: Nitish <[email protected]> --------- Signed-off-by: Nitish <[email protected]>
- Loading branch information
Showing
8 changed files
with
313 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
#include <cstdlib> | ||
#include <filesystem> | ||
#include <sstream> | ||
|
||
#include "lib/nullstream.h" | ||
#include "options.h" | ||
#include "p4fmt.h" | ||
|
||
int main(int argc, char *const argv[]) { | ||
AutoCompileContext autoP4FmtContext(new P4Fmt::P4FmtContext); | ||
auto &options = P4Fmt::P4FmtContext::get().options(); | ||
if (options.process(argc, argv) == nullptr) { | ||
return EXIT_FAILURE; | ||
} | ||
options.setInputFile(); | ||
|
||
std::stringstream formattedOutput = getFormattedOutput(options.file); | ||
if (formattedOutput.str().empty()) { | ||
return EXIT_FAILURE; | ||
}; | ||
|
||
std::ostream *out = nullptr; | ||
// Write to stdout in absence of an output file. | ||
if (options.outputFile().empty()) { | ||
out = &std::cout; | ||
} else { | ||
out = openFile(options.outputFile(), false); | ||
if ((out == nullptr) || !(*out)) { | ||
::error(ErrorType::ERR_NOT_FOUND, "%2%: No such file or directory.", | ||
options.outputFile().string()); | ||
options.usage(); | ||
return EXIT_FAILURE; | ||
} | ||
} | ||
|
||
(*out) << formattedOutput.str(); | ||
out->flush(); | ||
if (!(*out)) { | ||
::error(ErrorType::ERR_IO, "Failed to write to output file."); | ||
return EXIT_FAILURE; | ||
} | ||
|
||
return EXIT_SUCCESS; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,51 +1,35 @@ | ||
#include <cstdlib> | ||
#include "backends/p4fmt/p4fmt.h" | ||
|
||
#include "frontends/common/parseInput.h" | ||
#include "frontends/common/parser_options.h" | ||
#include "frontends/p4/toP4/toP4.h" | ||
#include "ir/ir.h" | ||
#include "lib/compile_context.h" | ||
#include "lib/cstring.h" | ||
#include "lib/error.h" | ||
#include "lib/nullstream.h" | ||
#include "options.h" | ||
|
||
int main(int argc, char *const argv[]) { | ||
std::stringstream getFormattedOutput(std::filesystem::path inputFile) { | ||
AutoCompileContext autoP4FmtContext(new P4Fmt::P4FmtContext); | ||
auto &options = P4Fmt::P4FmtContext::get().options(); | ||
|
||
if (options.process(argc, argv) == nullptr) { | ||
return EXIT_FAILURE; | ||
} | ||
options.file = std::move(inputFile); | ||
|
||
options.setInputFile(); | ||
|
||
std::ostream *out = nullptr; | ||
|
||
// Write to stdout in absence of an output file. | ||
if (options.outputFile().empty()) { | ||
out = &std::cout; | ||
} else { | ||
out = openFile(options.outputFile(), false); | ||
if (!(*out)) { | ||
::error(ErrorType::ERR_NOT_FOUND, "%2%: No such file or directory.", | ||
options.outputFile().string()); | ||
options.usage(); | ||
return EXIT_FAILURE; | ||
} | ||
} | ||
std::stringstream formattedOutput; | ||
|
||
const IR::P4Program *program = P4::parseP4File(options); | ||
|
||
if (program == nullptr && ::errorCount() != 0) { | ||
return EXIT_FAILURE; | ||
::error("Failed to parse P4 file."); | ||
return formattedOutput; | ||
} | ||
|
||
auto top4 = P4::ToP4(out, false); | ||
|
||
*out << "\n############################## INITIAL ##############################\n"; | ||
auto top4 = P4::ToP4(&formattedOutput, false); | ||
// Print the program before running front end passes. | ||
program->apply(top4); | ||
|
||
return ::errorCount() > 0 ? EXIT_FAILURE : EXIT_SUCCESS; | ||
if (::errorCount() > 0) { | ||
::error("Failed to format p4 program."); | ||
return formattedOutput; | ||
} | ||
|
||
return formattedOutput; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#ifndef BACKENDS_P4FMT_P4FMT_H_ | ||
#define BACKENDS_P4FMT_P4FMT_H_ | ||
|
||
#include <filesystem> | ||
#include <sstream> | ||
|
||
/// Formats a P4 program from the input file, returns formatted output | ||
std::stringstream getFormattedOutput(std::filesystem::path inputFile); | ||
|
||
#endif /* BACKENDS_P4FMT_P4FMT_H_ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/* -*- P4_16 -*- */ | ||
|
||
const bit<16> TYPE_IPV4 = 0x800; | ||
|
||
typedef bit<9> egressSpec_t; | ||
typedef bit<48> macAddr_t; | ||
typedef bit<32> ip4Addr_t; | ||
|
||
header ethernet_t { | ||
macAddr_t dstAddr; | ||
|
||
// comm1 | ||
macAddr_t srcAddr; // comm2 | ||
|
||
bit<16> etherType; | ||
} | ||
|
||
struct headers { | ||
ethernet_t ethernet; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
// Ref: https://github.com/fruffy/flay/blob/master/tools/reference_checker.cpp | ||
|
||
#include <cstdlib> | ||
#include <filesystem> | ||
#include <fstream> | ||
#include <optional> | ||
|
||
#include "backends/p4fmt/p4fmt.h" | ||
#include "backends/p4tools/common/lib/logging.h" | ||
#include "options.h" | ||
#include "p4fmt.h" | ||
|
||
namespace P4Fmt { | ||
|
||
namespace { | ||
class ReferenceCheckerOptions : protected P4fmtOptions { | ||
/// The input file to process | ||
std::optional<std::filesystem::path> inputFile; | ||
|
||
/// The reference file to compare against. | ||
std::optional<std::filesystem::path> referenceFile; | ||
|
||
/// Overwrite the reference file and do not compare against it. | ||
bool overwriteReferences = false; | ||
|
||
public: | ||
ReferenceCheckerOptions() { | ||
registerOption( | ||
"--file", "inputFile", | ||
[this](const char *arg) { | ||
inputFile = arg; | ||
if (!std::filesystem::exists(inputFile.value())) { | ||
::error("The input P4 program '%s' does not exist.", inputFile.value().c_str()); | ||
return false; | ||
} | ||
return true; | ||
}, | ||
"The input file to process."); | ||
registerOption( | ||
"--overwrite", nullptr, | ||
[this](const char *) { | ||
overwriteReferences = true; | ||
return true; | ||
}, | ||
"Do not check references, instead overwrite the reference."); | ||
registerOption( | ||
"--reference-file", "referenceFile", | ||
[this](const char *arg) { | ||
referenceFile = arg; | ||
return true; | ||
}, | ||
"The reference file to compare against."); | ||
} | ||
~ReferenceCheckerOptions() override = default; | ||
|
||
int processOptions(int argc, char *argv[]) { | ||
auto *unprocessedOptions = P4fmtOptions::process(argc, argv); | ||
|
||
if (unprocessedOptions != nullptr && !unprocessedOptions->empty()) { | ||
for (const auto &option : *unprocessedOptions) { | ||
::error("Unprocessed input: %s", option); | ||
} | ||
return EXIT_FAILURE; | ||
} | ||
|
||
if (!inputFile.has_value()) { | ||
::error("No input file specified."); | ||
return EXIT_FAILURE; | ||
} | ||
if (!referenceFile.has_value()) { | ||
::error( | ||
"Reference file has not been specified. Use " | ||
"--reference-file."); | ||
return EXIT_FAILURE; | ||
} | ||
// If the environment variable P4FMT_REPLACE is set, overwrite the reference file. | ||
if (std::getenv("P4FMT_REPLACE") != nullptr) { | ||
overwriteReferences = true; | ||
} | ||
return EXIT_SUCCESS; | ||
} | ||
|
||
[[nodiscard]] const std::filesystem::path &getInputFile() const { return inputFile.value(); } | ||
|
||
[[nodiscard]] std::optional<std::filesystem::path> getReferenceFile() const { | ||
return referenceFile; | ||
} | ||
|
||
[[nodiscard]] bool doOverwriteReferences() const { return overwriteReferences; } | ||
}; | ||
|
||
int compareAgainstReference(const std::stringstream &formattedOutput, | ||
const std::filesystem::path &referenceFile) { | ||
// Construct the command to invoke the diff utility | ||
std::stringstream command; | ||
command << "echo " << std::quoted(formattedOutput.str()); | ||
command << "| diff --color -u " << referenceFile.c_str() << " -"; | ||
|
||
// Open a pipe to capture the output of the diff command. | ||
P4Tools::printInfo("Running diff command: \"%s\"", command.str()); | ||
FILE *pipe = popen(command.str().c_str(), "r"); | ||
if (pipe == nullptr) { | ||
::error("Unable to create pipe to diff command."); | ||
return EXIT_FAILURE; | ||
} | ||
// Read and print the output of the diff command. | ||
std::stringstream result; | ||
char buffer[128]; | ||
while (fgets(buffer, sizeof(buffer), pipe) != nullptr) { | ||
result << buffer; | ||
} | ||
if (pclose(pipe) != 0) { | ||
::error("Diff command failed.\n%1%", result.str()); | ||
return EXIT_FAILURE; | ||
} | ||
return EXIT_SUCCESS; | ||
} | ||
|
||
std::optional<std::filesystem::path> getFilePath(const ReferenceCheckerOptions &options, | ||
const std::filesystem::path &basePath, | ||
std::string_view suffix) { | ||
auto referenceFileOpt = options.getReferenceFile(); | ||
auto referencePath = basePath; | ||
if (referenceFileOpt.has_value()) { | ||
// If a reference file is explicitly provided, just overwrite this file. | ||
referencePath = referenceFileOpt.value(); | ||
} else { | ||
::error("Reference file has not been specified."); | ||
return std::nullopt; | ||
} | ||
return referencePath.replace_extension(suffix); | ||
} | ||
|
||
} // namespace | ||
|
||
int run(const ReferenceCheckerOptions &options) { | ||
auto referenceFileOpt = options.getReferenceFile(); | ||
|
||
std::stringstream formattedOutput = getFormattedOutput(options.getInputFile()); | ||
|
||
if (formattedOutput.str().empty()) { | ||
::error("Formatting Failed"); | ||
return EXIT_FAILURE; | ||
} | ||
|
||
if (options.doOverwriteReferences()) { | ||
auto referencePath = getFilePath(options, options.getInputFile().stem(), ".ref"); | ||
if (!referencePath.has_value()) { | ||
return EXIT_FAILURE; | ||
} | ||
|
||
std::ofstream ofs(referencePath.value()); | ||
ofs << formattedOutput << std::endl; | ||
P4Tools::printInfo("Writing reference file %s", referencePath.value().c_str()); | ||
ofs.close(); | ||
return EXIT_SUCCESS; | ||
} | ||
if (referenceFileOpt.has_value()) { | ||
auto referenceFile = std::filesystem::absolute(referenceFileOpt.value()); | ||
return compareAgainstReference(formattedOutput, referenceFile); | ||
} | ||
::error("Reference file has not been specified."); | ||
return EXIT_FAILURE; | ||
} | ||
} // namespace P4Fmt | ||
|
||
class RefCheckContext : public BaseCompileContext {}; | ||
|
||
int main(int argc, char *argv[]) { | ||
AutoCompileContext autoP4RefCheckContext(new RefCheckContext); | ||
P4Fmt::ReferenceCheckerOptions options; | ||
|
||
if (options.processOptions(argc, argv) == EXIT_FAILURE || ::errorCount() != 0) { | ||
return EXIT_FAILURE; | ||
} | ||
|
||
auto result = P4Fmt::run(options); | ||
if (result == EXIT_FAILURE) { | ||
return EXIT_FAILURE; | ||
} | ||
return ::errorCount() == 0 ? EXIT_SUCCESS : EXIT_FAILURE; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/* -*- P4_16 -*- */ | ||
|
||
const bit<16> TYPE_IPV4 = 0x800; | ||
|
||
typedef bit<9> egressSpec_t; | ||
typedef bit<48> macAddr_t; | ||
typedef bit<32> ip4Addr_t; | ||
|
||
header ethernet_t { | ||
macAddr_t dstAddr; | ||
|
||
// comm1 | ||
macAddr_t srcAddr; // comm2 | ||
|
||
bit<16> etherType; | ||
} | ||
|
||
struct headers { | ||
ethernet_t ethernet; | ||
} |