Skip to content
Open
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
3 changes: 2 additions & 1 deletion src/gui/include/gui/gui.h
Original file line number Diff line number Diff line change
Expand Up @@ -1023,7 +1023,7 @@ class Gui

void registerHeatMap(HeatMapDataSource* heatmap);
void unregisterHeatMap(HeatMapDataSource* heatmap);
const std::set<HeatMapDataSource*>& getHeatMaps() { return heat_maps_; }
const std::set<HeatMapDataSource*>& getHeatMaps();
HeatMapDataSource* getHeatMap(const std::string& name);

// returns the Gui singleton
Expand Down Expand Up @@ -1060,6 +1060,7 @@ class Gui

private:
Gui();
void syncHeatMapChips();

void registerDescriptor(const std::type_info& type,
const Descriptor* descriptor);
Expand Down
24 changes: 24 additions & 0 deletions src/gui/src/gui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1016,8 +1016,32 @@ void Gui::unregisterHeatMap(HeatMapDataSource* heatmap)
heat_maps_.erase(heatmap);
}

void Gui::syncHeatMapChips()
{
if (hasUI() || db_ == nullptr) {
return;
}

// Console and headless sessions do not receive MainWindow::setBlock().
auto* chip = db_->getChip();
for (auto* heat_map : heat_maps_) {
if (heat_map->getChip() != chip) {
heat_map->setChip(chip);
heat_map->destroyMap();
}
}
}
Comment on lines +1021 to +1033
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The syncHeatMapChips method correctly handles the synchronization of heatmap data sources with the current database chip in headless mode. However, for better API consistency and to ensure that any code iterating over all heatmaps (via getHeatMaps()) also sees the updated state, consider moving the implementation of getHeatMaps() from the header to the .cpp file and calling syncHeatMapChips() there as well.


const std::set<HeatMapDataSource*>& Gui::getHeatMaps()
{
syncHeatMapChips();
return heat_maps_;
}

HeatMapDataSource* Gui::getHeatMap(const std::string& name)
{
syncHeatMapChips();

HeatMapDataSource* source = nullptr;

for (auto* heat_map : heat_maps_) {
Expand Down
17 changes: 17 additions & 0 deletions src/gui/test/BUILD
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
load("@rules_cc//cc:cc_test.bzl", "cc_test")
load("@rules_python//python:defs.bzl", "py_test")

# SPDX-License-Identifier: BSD-3-Clause
Expand All @@ -11,6 +12,10 @@ ALL_TESTS = [
"supported",
]

# dump_heatmap_headless requires the full Qt GUI implementation. The default
# Bazel openroad binary links the stub GUI, so the equivalent Bazel regression is
# the C++ heatmap_sync_test against //src/gui:gui_qt_headless below.

filegroup(
name = "test_resources",
# overly broad glob, could be refined later, but
Expand Down Expand Up @@ -54,3 +59,15 @@ doc_check_test(
],
visibility = ["//visibility:public"],
)

cc_test(
name = "heatmap_sync_test",
srcs = ["heatmap_sync_test.cpp"],
deps = [
"//src/gui:gui_qt_headless",
"//src/odb",
"//src/utl",
"@googletest//:gtest",
"@googletest//:gtest_main",
],
)
19 changes: 17 additions & 2 deletions src/gui/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
set(GUI_TESTS supported)

if (Qt5_FOUND AND BUILD_GUI)
list(APPEND GUI_TESTS dump_heatmap_headless)

add_executable(gui_heatmap_sync_test heatmap_sync_test.cpp)
target_link_libraries(gui_heatmap_sync_test
gui
odb
utl_lib
GTest::gtest
GTest::gtest_main
)
gtest_discover_tests(gui_heatmap_sync_test)
endif()

or_integration_tests(
"gui"
TESTS
supported
${GUI_TESTS}
)

1 change: 1 addition & 0 deletions src/gui/test/dump_heatmap_headless.ok
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Pass
35 changes: 35 additions & 0 deletions src/gui/test/dump_heatmap_headless.tcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
set script_dir [file dirname [info script]]
set openroad_test_dir [file normalize [file join $script_dir .. .. .. test]]

source [file join $openroad_test_dir helpers.tcl]

if { ![gui::supported] } {
puts "Fail"
exit 1
}

suppress_message ODB 128
suppress_message ODB 130
suppress_message ODB 131
suppress_message ODB 132
suppress_message ODB 133
suppress_message ODB 227

read_lef [file join $openroad_test_dir Nangate45 Nangate45.lef]
read_def [file join $openroad_test_dir gcd_nangate45.def]

set heatmap_file [make_result_file dump_heatmap_headless.csv]
gui::dump_heatmap Placement $heatmap_file

set heatmap [open $heatmap_file r]
set contents [read $heatmap]
close $heatmap

if {
[string first "value (%)" $contents] >= 0
&& [regexp -line {^[0-9.-]+,[0-9.-]+,[0-9.-]+,[0-9.-]+,} $contents]
} {
puts "Pass"
} else {
puts "Fail"
}
43 changes: 43 additions & 0 deletions src/gui/test/heatmap_sync_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2026, The OpenROAD Authors

#include "gtest/gtest.h"
#include "gui/gui.h"
#include "gui/heatMap.h"
#include "odb/db.h"
#include "utl/Logger.h"

namespace gui {
namespace {

TEST(GuiHeatMapTest, HeadlessLookupSyncsSourcesToCurrentChip)
{
utl::Logger logger;
odb::dbDatabase* db = odb::dbDatabase::create();
ASSERT_NE(db, nullptr);

Gui* gui = Gui::get();
gui->init(db, nullptr, &logger);

HeatMapDataSource* placement = gui->getHeatMap("Placement");
ASSERT_NE(placement, nullptr);
EXPECT_EQ(nullptr, placement->getChip());

odb::dbTech* tech = odb::dbTech::create(db, "tech");
ASSERT_NE(tech, nullptr);
odb::dbChip* chip = odb::dbChip::create(db, tech);
ASSERT_NE(chip, nullptr);
odb::dbBlock::create(chip, "top");

EXPECT_EQ(chip, db->getChip());
EXPECT_EQ(chip, gui->getHeatMap("Placement")->getChip());

const auto& heat_maps = gui->getHeatMaps();
ASSERT_FALSE(heat_maps.empty());
for (auto* heat_map : heat_maps) {
EXPECT_EQ(chip, heat_map->getChip());
}
}

} // namespace
} // namespace gui
Loading