Skip to content

Commit b0614f9

Browse files
committed
Add the ability to append to the result json file for wallclock benchmarks (AI Generated)
1 parent 57faaec commit b0614f9

1 file changed

Lines changed: 118 additions & 46 deletions

File tree

core/src/walltime.cpp

Lines changed: 118 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -59,30 +59,8 @@ static std::string escape_backslashes(const std::string &input) {
5959
return output;
6060
}
6161

62-
static void write_codspeed_benchmarks_to_json(
63-
const std::vector<CodspeedWalltimeBenchmark> &benchmarks) {
62+
static std::string serialize_benchmark_objects(const std::vector<CodspeedWalltimeBenchmark> &benchmarks) {
6463
std::ostringstream oss;
65-
66-
std::string creator_name = "codspeed-cpp";
67-
std::string creator_version = CODSPEED_VERSION;
68-
#ifdef _WIN32
69-
auto creator_pid = _getpid();
70-
#else
71-
pid_t creator_pid = getpid();
72-
#endif
73-
std::string instrument_type = "walltime";
74-
75-
oss << "{\n";
76-
oss << " \"creator\": {\n";
77-
oss << " \"name\": \"" << creator_name << "\",\n";
78-
oss << " \"version\": \"" << creator_version << "\",\n";
79-
oss << " \"pid\": " << creator_pid << "\n";
80-
oss << " },\n";
81-
oss << " \"instrument\": {\n";
82-
oss << " \"type\": \"" << instrument_type << "\"\n";
83-
oss << " },\n";
84-
oss << " \"benchmarks\": [\n";
85-
8664
for (size_t i = 0; i < benchmarks.size(); ++i) {
8765
const auto &benchmark = benchmarks[i];
8866
const auto &stats = benchmark.stats;
@@ -118,40 +96,134 @@ static void write_codspeed_benchmarks_to_json(
11896
oss << " }";
11997

12098
if (i < benchmarks.size() - 1) {
121-
oss << ",";
99+
oss << ",\n";
122100
}
123-
oss << "\n";
124101
}
102+
return oss.str();
103+
}
125104

126-
oss << " ]\n";
127-
oss << "}";
128-
129-
// Determine the directory path
105+
static std::filesystem::path get_results_path() {
130106
std::string profile_folder = safe_getenv("CODSPEED_PROFILE_FOLDER");
131107
std::string directory = profile_folder.empty() ? "." : profile_folder;
108+
return std::filesystem::path(directory) / "results" / "benchmarks.json";
109+
}
110+
111+
static void write_codspeed_benchmarks_to_json(
112+
const std::vector<CodspeedWalltimeBenchmark> &benchmarks) {
113+
114+
std::filesystem::path file_path = get_results_path();
115+
std::filesystem::create_directories(file_path.parent_path());
116+
117+
std::ofstream out(file_path, std::ios::trunc);
118+
if (!out) {
119+
std::cerr << "Failed to open file for writing: " << file_path << std::endl;
120+
return;
121+
}
132122

133-
// Create the results directory if it does not exist
134-
std::filesystem::path results_path = directory + "/results";
135-
if (!std::filesystem::exists(results_path)) {
136-
if (!std::filesystem::create_directories(results_path)) {
137-
std::cerr << "Failed to create directory: " << results_path << std::endl;
138-
return;
123+
std::string creator_name = "codspeed-cpp";
124+
std::string creator_version = CODSPEED_VERSION;
125+
#ifdef _WIN32
126+
auto creator_pid = _getpid();
127+
#else
128+
pid_t creator_pid = getpid();
129+
#endif
130+
std::string instrument_type = "walltime";
131+
132+
out << "{\n";
133+
out << " \"creator\": {\n";
134+
out << " \"name\": \"" << creator_name << "\",\n";
135+
out << " \"version\": \"" << creator_version << "\",\n";
136+
out << " \"pid\": " << creator_pid << "\n";
137+
out << " },\n";
138+
out << " \"instrument\": {\n";
139+
out << " \"type\": \"" << instrument_type << "\"\n";
140+
out << " },\n";
141+
out << " \"benchmarks\": [\n";
142+
143+
out << serialize_benchmark_objects(benchmarks);
144+
145+
out << "\n ]\n}";
146+
}
147+
148+
static void append_codspeed_benchmarks_to_json(
149+
const std::vector<CodspeedWalltimeBenchmark> &benchmarks) {
150+
151+
if (benchmarks.empty()) return;
152+
153+
std::filesystem::path file_path = get_results_path();
154+
155+
// Condition: If file is missing or entirely empty, generate fresh
156+
if (!std::filesystem::exists(file_path) || std::filesystem::file_size(file_path) == 0) {
157+
write_codspeed_benchmarks_to_json(benchmarks);
158+
return;
159+
}
160+
161+
// Open in read/write binary mode to prevent Windows newline conversion from messing with stream offsets
162+
std::fstream file(file_path, std::ios::in | std::ios::out | std::ios::binary);
163+
if (!file) {
164+
std::cerr << "Failed to open file for appending: " << file_path << std::endl;
165+
return;
166+
}
167+
168+
// Back up from the EOF until we strip away '}', ']', and whitespace
169+
file.seekp(0, std::ios::end);
170+
std::streampos pos = file.tellp();
171+
172+
int brackets_found = 0;
173+
bool array_was_empty = false;
174+
std::streampos truncation_pos = 0;
175+
176+
// Read backwards to safely find where the trailing array structural markers close
177+
while (pos > 0) {
178+
pos -= 1;
179+
file.seekg(pos);
180+
char ch;
181+
file.get(ch);
182+
183+
if (std::isspace(static_cast<unsigned char>(ch))) {
184+
continue;
185+
}
186+
187+
if (brackets_found == 0 && ch == '}') {
188+
brackets_found = 1;
189+
continue;
190+
}
191+
192+
if (brackets_found == 1 && ch == ']') {
193+
brackets_found = 2;
194+
continue;
139195
}
196+
197+
if (brackets_found == 2) {
198+
// Check if the last character before whitespace is the open bracket '[' (meaning array was empty)
199+
if (ch == '[') {
200+
array_was_empty = true;
201+
}
202+
truncation_pos = pos + std::streamoff(1);
203+
break;
204+
}
205+
}
206+
207+
if (brackets_found < 2) {
208+
std::cerr << "Failed to find valid JSON structural endings to safely append." << std::endl;
209+
return;
140210
}
141211

142-
// Create the file path
143-
std::ostringstream file_path;
144-
file_path << results_path.string() << "/" << creator_pid << ".json";
212+
file.seekp(truncation_pos);
145213

146-
// Write to file
147-
std::ofstream out_file(file_path.str());
148-
if (out_file.is_open()) {
149-
out_file << oss.str();
150-
out_file.close();
151-
std::cout << "JSON written to " << file_path.str() << std::endl;
214+
if (!array_was_empty) {
215+
file << ",\n"; // Commas are mandatory between pre-existing elements
152216
} else {
153-
std::cerr << "Unable to open file " << file_path.str() << std::endl;
217+
file << "\n";
154218
}
219+
220+
// Inject the new items
221+
file << serialize_benchmark_objects(benchmarks);
222+
223+
// Re-close the JSON root safely
224+
file << "\n ]\n}";
225+
226+
std::filesystem::resize_file(file_path, file.tellp());
155227
}
156228

157229
BenchmarkStats compute_benchmark_stats(
@@ -245,7 +317,7 @@ void generate_codspeed_walltime_report(
245317
codspeed_walltime_benchmarks.push_back(codspeed_benchmark);
246318
}
247319

248-
write_codspeed_benchmarks_to_json(codspeed_walltime_benchmarks);
320+
append_codspeed_benchmarks_to_json(codspeed_walltime_benchmarks);
249321
}
250322

251323
} // namespace codspeed

0 commit comments

Comments
 (0)