Skip to content

Commit 3daea17

Browse files
authored
Merge pull request #8705 from calewis/suppress_stdout
Utility to temporarily suppress stdout
2 parents 5581142 + 3a53a7e commit 3daea17

File tree

8 files changed

+182
-34
lines changed

8 files changed

+182
-34
lines changed

src/rmp/src/annealing_strategy.cpp

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "sta/Search.hh"
3434
#include "utils.h"
3535
#include "utl/Logger.h"
36+
#include "utl/SuppressStdout.h"
3637
#include "utl/deleter.h"
3738
#include "utl/unique_name.h"
3839

@@ -76,37 +77,6 @@ extern void Abc_NtkRedirectCiCo(Abc_Ntk_t* pNtk);
7677
namespace rmp {
7778
using utl::RMP;
7879

79-
class SuppressStdout
80-
{
81-
#ifndef _WIN32
82-
public:
83-
SuppressStdout(utl::Logger* logger)
84-
{
85-
// This is a hack to suppress excessive logs from ABC
86-
// Redirects stdout to /dev/null, preserves original stdout
87-
fflush(stdout);
88-
saved_stdout_fd = dup(1);
89-
const int dev_null_fd = open("/dev/null", O_WRONLY);
90-
if (dev_null_fd < 0) {
91-
logger->error(utl::RMP, 58, "Can't open /dev/null");
92-
}
93-
dup2(dev_null_fd, 1);
94-
close(dev_null_fd);
95-
}
96-
97-
~SuppressStdout()
98-
{
99-
// Restore stdout
100-
fflush(stdout);
101-
dup2(saved_stdout_fd, 1);
102-
close(saved_stdout_fd);
103-
}
104-
105-
private:
106-
int saved_stdout_fd;
107-
#endif
108-
};
109-
11080
static void replaceGia(abc::Gia_Man_t*& gia, abc::Gia_Man_t* new_gia)
11181
{
11282
if (gia == new_gia) {
@@ -224,7 +194,7 @@ void AnnealingStrategy::OptimizeDesign(sta::dbSta* sta,
224194
// &false
225195
debugPrint(
226196
logger, RMP, "annealing", 1, "Starting false path elimination");
227-
SuppressStdout nostdout(logger);
197+
utl::SuppressStdout nostdout(logger);
228198
replaceGia(gia, Gia_ManCheckFalse(gia, 0, 0, false, false));
229199
},
230200

@@ -637,7 +607,7 @@ void AnnealingStrategy::RunGia(
637607
}
638608

639609
{
640-
SuppressStdout nostdout(logger);
610+
utl::SuppressStdout nostdout(logger);
641611
current_network = WrapUnique(abc::Abc_NtkMap(current_network.get(),
642612
nullptr,
643613
/*DelayTarget=*/1.0,
@@ -661,7 +631,7 @@ void AnnealingStrategy::RunGia(
661631

662632
if (resize_iters > 0) {
663633
// All the magic numbers are defaults from abc/src/base/abci/abc.c
664-
SuppressStdout nostdout(logger);
634+
utl::SuppressStdout nostdout(logger);
665635
abc::SC_SizePars pars = {};
666636
pars.nIters = resize_iters;
667637
pars.nIterNoChange = 50;

src/utl/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ cc_library(
2828
"src/Metrics.cpp",
2929
"src/Progress.cpp",
3030
"src/ScopedTemporaryFile.cpp",
31+
"src/SuppressStdout.cpp",
3132
"src/decode.cpp",
3233
"src/histogram.cpp",
3334
"src/mem_stats.cpp",
@@ -42,6 +43,7 @@ cc_library(
4243
"include/utl/Metrics.h",
4344
"include/utl/Progress.h",
4445
"include/utl/ScopedTemporaryFile.h",
46+
"include/utl/SuppressStdout.h",
4547
"include/utl/algorithms.h",
4648
"include/utl/decode.h",
4749
"include/utl/deleter.h",

src/utl/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ add_library(utl_lib
4141
src/Metrics.cpp
4242
src/CFileUtils.cpp
4343
src/ScopedTemporaryFile.cpp
44+
src/SuppressStdout.cpp
4445
src/Logger.cpp
4546
src/Progress.cpp
4647
src/CommandLineProgress.cpp
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
// Copyright (c) 2025, The OpenROAD Authors
3+
4+
#pragma once
5+
6+
#include "utl/Logger.h"
7+
8+
namespace utl {
9+
10+
// Redirects stdout to /dev/null while in scope
11+
class SuppressStdout
12+
{
13+
public:
14+
SuppressStdout(Logger* logger);
15+
~SuppressStdout();
16+
17+
// Don't copy or move
18+
SuppressStdout(SuppressStdout const&) = delete;
19+
SuppressStdout(SuppressStdout&&) = delete;
20+
SuppressStdout& operator=(SuppressStdout const&) = delete;
21+
SuppressStdout& operator=(SuppressStdout&&) = delete;
22+
23+
private:
24+
int saved_fd_ = -1;
25+
Logger* logger_ = nullptr;
26+
};
27+
28+
} // namespace utl

src/utl/src/SuppressStdout.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#include "utl/SuppressStdout.h"
2+
3+
#include <fcntl.h>
4+
#include <unistd.h>
5+
6+
#include <cstdio>
7+
8+
#include "utl/Logger.h"
9+
10+
namespace utl {
11+
12+
SuppressStdout::SuppressStdout(utl::Logger* logger) : logger_(logger)
13+
{
14+
fflush(stdout);
15+
16+
saved_fd_ = dup(STDOUT_FILENO);
17+
if (saved_fd_ < 0) {
18+
logger_->error(UTL, 73, "Failed to dup STDOUT_FILENO");
19+
return;
20+
}
21+
22+
const int dev_null_fd = open("/dev/null", O_WRONLY);
23+
if (dev_null_fd < 0) {
24+
close(saved_fd_);
25+
saved_fd_ = -1;
26+
logger_->error(UTL, 74, "Failed to open /dev/null");
27+
return;
28+
}
29+
30+
if (dup2(dev_null_fd, STDOUT_FILENO) < 0) {
31+
close(saved_fd_);
32+
saved_fd_ = -1;
33+
logger_->error(UTL, 75, "dup2 failed to redirect STDOUT_FILENO");
34+
}
35+
36+
close(dev_null_fd);
37+
}
38+
39+
SuppressStdout::~SuppressStdout()
40+
{
41+
fflush(stdout);
42+
if (saved_fd_ == -1) {
43+
return;
44+
}
45+
46+
if (dup2(saved_fd_, STDOUT_FILENO) < 0) {
47+
logger_->error(UTL,
48+
76,
49+
"dup2 failed to restore stdout, all stdout is likely broken "
50+
"from this point forward.");
51+
} else {
52+
close(saved_fd_);
53+
}
54+
}
55+
56+
} // namespace utl

src/utl/test/BUILD

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,13 @@ cc_test(
9898
"@googletest//:gtest_main",
9999
],
100100
)
101+
102+
cc_test(
103+
name = "TestSuppressStdout",
104+
srcs = ["cpp/TestSuppressStdout.cpp"],
105+
deps = [
106+
"//src/utl",
107+
"@googletest//:gtest",
108+
"@googletest//:gtest_main",
109+
],
110+
)

src/utl/test/cpp/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,15 @@ gtest_discover_tests(TestMetrics
3131
add_dependencies(build_and_test
3232
TestMetrics
3333
)
34+
35+
add_executable(TestSuppressStdout TestSuppressStdout.cpp)
36+
37+
target_link_libraries(TestSuppressStdout ${TEST_LIBS})
38+
39+
gtest_discover_tests(TestSuppressStdout
40+
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
41+
)
42+
43+
add_dependencies(build_and_test
44+
TestSuppressStdout
45+
)
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
// Copyright (c) 2024, The OpenROAD Authors
3+
4+
#include <fcntl.h>
5+
#include <unistd.h>
6+
7+
#include <cstdio>
8+
#include <fstream>
9+
#include <string>
10+
11+
#include "gtest/gtest.h"
12+
#include "utl/Logger.h"
13+
#include "utl/SuppressStdout.h"
14+
15+
namespace utl {
16+
17+
TEST(Utl, SuppressStdout)
18+
{
19+
Logger logger;
20+
21+
// 1. Create a temporary directory managed by GTest
22+
auto temp_dir = testing::TempDir();
23+
std::string file_path = temp_dir + "/my_file.txt";
24+
25+
// 3. Create the file and get the file descriptor (fd)
26+
int fd = open(file_path.c_str(), O_WRONLY | O_CREAT, 0600);
27+
ASSERT_NE(fd, -1);
28+
29+
int saved_stdout = dup(STDOUT_FILENO);
30+
ASSERT_NE(saved_stdout, -1);
31+
32+
ASSERT_NE(dup2(fd, STDOUT_FILENO), -1);
33+
34+
fprintf(stdout, "Before suppression\n");
35+
{
36+
SuppressStdout so(&logger);
37+
fprintf(stdout, "During suppression\n");
38+
}
39+
40+
fprintf(stdout, "After suppression\n");
41+
fflush(stdout);
42+
43+
close(fd);
44+
dup2(saved_stdout, STDOUT_FILENO);
45+
close(saved_stdout);
46+
47+
std::ifstream log_file(file_path);
48+
std::string line;
49+
50+
bool before_found = false;
51+
bool during_found = false;
52+
bool after_found = false;
53+
54+
while (std::getline(log_file, line)) {
55+
if (line == "Before suppression") {
56+
before_found = true;
57+
} else if (line == "During suppression") {
58+
during_found = true;
59+
} else if (line == "After suppression") {
60+
after_found = true;
61+
}
62+
}
63+
64+
EXPECT_TRUE(before_found);
65+
EXPECT_FALSE(during_found);
66+
EXPECT_TRUE(after_found);
67+
}
68+
69+
} // namespace utl

0 commit comments

Comments
 (0)