Skip to content

Commit 165fe36

Browse files
tanjialiangmeta-codesync[bot]
authored andcommitted
feat: Add user friendly trace prompting API (#16192)
Summary: Pull Request resolved: #16192 ### Added User Friendly Trace Prompting API This diff introduces a new feature that provides a user-friendly trace prompting API. The changes include: #### New Files Added * `TraceReplayerConfigPrompt.cpp`: a new file that contains the implementation of the trace replayer config prompt. * `TraceReplayerConfigPrompt.h`: a new header file for the trace replayer config prompt. * `TraceReplayerConfigPromptTest.cpp`: a new test file for the trace replayer config prompt. #### Modifications to Existing Files * `BUCK`: updated to include the new files. * `TraceReplayRunner.cpp`: updated to include the new `TraceReplayerConfigPrompt` header and to call the `promptForConfigs` method. * `CMakeLists.txt`: updated to include the new test file. * `tests/BUCK`: updated to include the new test file. #### Features and Improvements * The new API provides a user-friendly way to prompt for configurations interactively, unless the `--fast` flag is set. * The `TraceReplayerConfigPrompt` class is responsible for prompting the user for configurations. * The `promptForConfigs` method is called in the `init` method of `TraceReplayRunner` to prompt the user for configurations. Overall, this diff aims to improve the user experience by providing a more interactive and user-friendly way to configure the trace replayer. Reviewed By: xiaoxmeng, zacw7 Differential Revision: D91977849 fbshipit-source-id: 71fe40f88855fa160c789e342eb44638429ab0ee
1 parent 12be762 commit 165fe36

File tree

6 files changed

+547
-0
lines changed

6 files changed

+547
-0
lines changed

velox/tool/trace/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ add_library(
2525
TableScanReplayer.cpp
2626
TableWriterReplayer.cpp
2727
TopNRowNumberReplayer.cpp
28+
TraceReplayerConfigPrompt.cpp
2829
TraceReplayRunner.cpp
2930
TraceReplayTaskRunner.cpp
3031
UnnestReplayer.cpp

velox/tool/trace/TraceReplayRunner.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
#include "velox/tool/trace/TableScanReplayer.h"
5454
#include "velox/tool/trace/TableWriterReplayer.h"
5555
#include "velox/tool/trace/TopNRowNumberReplayer.h"
56+
#include "velox/tool/trace/TraceReplayerConfigPrompt.h"
5657
#include "velox/tool/trace/UnnestReplayer.h"
5758
#include "velox/type/Type.h"
5859

@@ -266,6 +267,10 @@ TraceReplayRunner::~TraceReplayRunner() {
266267
}
267268

268269
void TraceReplayRunner::init() {
270+
// Prompt user for configs interactively (unless --fast is set)
271+
TraceReplayerConfigPrompt prompt;
272+
prompt.run();
273+
269274
VELOX_USER_CHECK(!FLAGS_root_dir.empty(), "--root_dir must be provided");
270275
VELOX_USER_CHECK(!FLAGS_node_id.empty(), "--node_id must be provided");
271276

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
/*
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "velox/tool/trace/TraceReplayerConfigPrompt.h"
18+
19+
#include <gflags/gflags.h>
20+
#include <iostream>
21+
#include <sstream>
22+
#include <string>
23+
24+
DEFINE_bool(
25+
fast,
26+
false,
27+
"Skip interactive prompts and use existing gflag values directly. "
28+
"Use this flag when you already know the config values and want to "
29+
"bypass the interactive configuration.");
30+
31+
DECLARE_string(root_dir);
32+
DECLARE_string(query_id);
33+
DECLARE_string(task_id);
34+
DECLARE_string(node_id);
35+
DECLARE_string(driver_ids);
36+
DECLARE_string(table_writer_output_dir);
37+
38+
namespace facebook::velox::tool::trace {
39+
40+
namespace {
41+
constexpr const char* kColorReset = "\033[0m";
42+
constexpr const char* kColorBold = "\033[1m";
43+
constexpr const char* kColorGreen = "\033[32m";
44+
constexpr const char* kColorYellow = "\033[33m";
45+
constexpr const char* kColorCyan = "\033[36m";
46+
47+
// Reads a line from stdin, trimming leading/trailing whitespace.
48+
std::string readLine() {
49+
std::string input;
50+
std::getline(std::cin, input);
51+
52+
// Trim leading whitespace
53+
size_t start = input.find_first_not_of(" \t\n\r");
54+
if (start == std::string::npos) {
55+
return "";
56+
}
57+
58+
// Trim trailing whitespace
59+
size_t end = input.find_last_not_of(" \t\n\r");
60+
return input.substr(start, end - start + 1);
61+
}
62+
63+
// Prints a separator line for visual clarity.
64+
void printSeparator() {
65+
std::cout << kColorCyan
66+
<< "============================================================"
67+
<< kColorReset << std::endl;
68+
}
69+
70+
// Prints a header for the config prompt session.
71+
void printHeader() {
72+
std::cout << std::endl;
73+
printSeparator();
74+
std::cout << kColorBold << kColorGreen
75+
<< " Trace Replayer Configuration Prompt" << kColorReset
76+
<< std::endl;
77+
printSeparator();
78+
std::cout << std::endl;
79+
std::cout << "Enter config values or press " << kColorYellow << "Enter"
80+
<< kColorReset << " to keep current gflag value." << std::endl;
81+
std::cout << std::endl;
82+
}
83+
84+
// Prints the equivalent command with --fast flag for future use.
85+
void printFastCommand() {
86+
std::ostringstream cmd;
87+
cmd << "--fast";
88+
89+
if (!FLAGS_root_dir.empty()) {
90+
cmd << " \\\n --root_dir=\"" << FLAGS_root_dir << "\"";
91+
}
92+
if (!FLAGS_query_id.empty()) {
93+
cmd << " \\\n --query_id=\"" << FLAGS_query_id << "\"";
94+
}
95+
if (!FLAGS_task_id.empty()) {
96+
cmd << " \\\n --task_id=\"" << FLAGS_task_id << "\"";
97+
}
98+
if (!FLAGS_node_id.empty()) {
99+
cmd << " \\\n --node_id=\"" << FLAGS_node_id << "\"";
100+
}
101+
if (!FLAGS_driver_ids.empty()) {
102+
cmd << " \\\n --driver_ids=\"" << FLAGS_driver_ids << "\"";
103+
}
104+
if (!FLAGS_table_writer_output_dir.empty()) {
105+
cmd << " \\\n --table_writer_output_dir=\""
106+
<< FLAGS_table_writer_output_dir << "\"";
107+
}
108+
109+
std::cout << std::endl;
110+
printSeparator();
111+
std::cout << kColorBold << kColorGreen << "Quick Command for Next Run"
112+
<< kColorReset << std::endl;
113+
printSeparator();
114+
std::cout << std::endl;
115+
std::cout
116+
<< "To skip prompts next time, copy and run with the following flags:\n"
117+
<< std::endl;
118+
std::cout << kColorYellow << cmd.str() << kColorReset << std::endl;
119+
std::cout << std::endl;
120+
printSeparator();
121+
std::cout << std::endl;
122+
}
123+
124+
// Prompts for a single config value.
125+
// @param configName The name of the config (for display purposes).
126+
// @param currentValue The current value from gflag.
127+
// @param description A brief description of what this config is for.
128+
// @param required Whether this config is required (cannot be empty).
129+
// @return The final value (either user input or the original gflag value).
130+
std::string promptForConfig(
131+
const std::string& configName,
132+
const std::string& currentValue,
133+
const std::string& description,
134+
bool required) {
135+
std::cout << kColorBold << configName << kColorReset << std::endl;
136+
std::cout << " " << description << std::endl;
137+
std::cout << " Current gflag value: " << kColorYellow
138+
<< (currentValue.empty() ? "(empty)" : currentValue) << kColorReset
139+
<< std::endl;
140+
141+
if (required) {
142+
std::cout << " " << kColorCyan << "[REQUIRED]" << kColorReset << std::endl;
143+
}
144+
145+
std::cout << " Enter new value: ";
146+
std::string input = readLine();
147+
148+
std::string finalValue = input.empty() ? currentValue : input;
149+
150+
if (required && finalValue.empty()) {
151+
std::cout << kColorYellow << " Warning: This config is required but empty."
152+
<< kColorReset << std::endl;
153+
}
154+
155+
std::cout << " Using: " << kColorGreen << finalValue << kColorReset
156+
<< std::endl;
157+
std::cout << std::endl;
158+
159+
return finalValue;
160+
}
161+
} // namespace
162+
163+
void TraceReplayerConfigPrompt::run() {
164+
if (FLAGS_fast) {
165+
std::cout << kColorCyan << "[--fast mode] "
166+
<< "Skipping interactive prompts, using existing gflag values."
167+
<< kColorReset << std::endl;
168+
std::cout << std::endl;
169+
170+
// Print summary of current config values
171+
std::cout << kColorBold << "Current Configuration:" << kColorReset
172+
<< std::endl;
173+
std::cout << " root_dir: " << FLAGS_root_dir << std::endl;
174+
std::cout << " query_id: " << FLAGS_query_id << std::endl;
175+
std::cout << " task_id: "
176+
<< (FLAGS_task_id.empty() ? "(empty - summary mode)"
177+
: FLAGS_task_id)
178+
<< std::endl;
179+
std::cout << " node_id: " << FLAGS_node_id << std::endl;
180+
std::cout << " driver_ids: "
181+
<< (FLAGS_driver_ids.empty() ? "(all drivers)" : FLAGS_driver_ids)
182+
<< std::endl;
183+
std::cout << " table_writer_output_dir: "
184+
<< (FLAGS_table_writer_output_dir.empty()
185+
? "(not set)"
186+
: FLAGS_table_writer_output_dir)
187+
<< std::endl;
188+
std::cout << std::endl;
189+
return;
190+
}
191+
192+
printHeader();
193+
194+
// Prompt for critical required configs
195+
FLAGS_root_dir = promptForConfig(
196+
"--root_dir",
197+
FLAGS_root_dir,
198+
"Root directory where the replayer reads traced data",
199+
true);
200+
201+
FLAGS_query_id = promptForConfig(
202+
"--query_id", FLAGS_query_id, "The target query id to replay", true);
203+
204+
FLAGS_task_id = promptForConfig(
205+
"--task_id",
206+
FLAGS_task_id,
207+
"The target task id to replay (leave empty for summary mode)",
208+
false);
209+
210+
FLAGS_node_id = promptForConfig(
211+
"--node_id", FLAGS_node_id, "The target node id to replay", true);
212+
213+
// Prompt for optional configs
214+
std::cout << kColorBold << "Optional Configs:" << kColorReset << std::endl;
215+
std::cout << std::endl;
216+
217+
FLAGS_driver_ids = promptForConfig(
218+
"--driver_ids",
219+
FLAGS_driver_ids,
220+
"A comma-separated list of target driver ids",
221+
false);
222+
223+
FLAGS_table_writer_output_dir = promptForConfig(
224+
"--table_writer_output_dir",
225+
FLAGS_table_writer_output_dir,
226+
"Output directory for TableWriter (required for TableWrite replays)",
227+
false);
228+
229+
printSeparator();
230+
std::cout << kColorGreen << "Configuration complete!" << kColorReset
231+
<< std::endl;
232+
std::cout << std::endl;
233+
234+
// Print summary
235+
std::cout << kColorBold << "Final Configuration Summary:" << kColorReset
236+
<< std::endl;
237+
std::cout << " root_dir: " << FLAGS_root_dir << std::endl;
238+
std::cout << " query_id: " << FLAGS_query_id << std::endl;
239+
std::cout << " task_id: "
240+
<< (FLAGS_task_id.empty() ? "(empty - summary mode)"
241+
: FLAGS_task_id)
242+
<< std::endl;
243+
std::cout << " node_id: " << FLAGS_node_id << std::endl;
244+
std::cout << " driver_ids: "
245+
<< (FLAGS_driver_ids.empty() ? "(all drivers)" : FLAGS_driver_ids)
246+
<< std::endl;
247+
std::cout << " table_writer_output_dir: "
248+
<< (FLAGS_table_writer_output_dir.empty()
249+
? "(not set)"
250+
: FLAGS_table_writer_output_dir)
251+
<< std::endl;
252+
253+
// Print the fast command for next time
254+
printFastCommand();
255+
}
256+
257+
} // namespace facebook::velox::tool::trace
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include <gflags/gflags.h>
20+
#include <string>
21+
22+
DECLARE_bool(fast);
23+
24+
namespace facebook::velox::tool::trace {
25+
26+
/// A utility class that prompts users to enter critical configs for trace
27+
/// replay. It displays the current gflag setting and allows users to override
28+
/// or confirm the value interactively.
29+
///
30+
/// When --fast flag is set, prompts are skipped and existing gflag values are
31+
/// used directly.
32+
class TraceReplayerConfigPrompt {
33+
public:
34+
TraceReplayerConfigPrompt() = default;
35+
~TraceReplayerConfigPrompt() = default;
36+
37+
/// Prompts the user to enter or confirm all critical configs for trace
38+
/// replay. This includes root_dir, query_id, task_id, and node_id.
39+
/// If a gflag is already set, it shows the current value and allows the user
40+
/// to press Enter to keep it or type a new value to override.
41+
///
42+
/// If --fast flag is set, skips all prompts and uses existing gflag values.
43+
/// After configuration, prints the equivalent command with --fast for future
44+
/// use.
45+
void run();
46+
};
47+
48+
} // namespace facebook::velox::tool::trace

velox/tool/trace/tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ add_executable(
2525
TableWriterReplayerTest.cpp
2626
TopNRowNumberReplayerTest.cpp
2727
TraceFileToolTest.cpp
28+
TraceReplayerConfigPromptTest.cpp
2829
UnnestReplayerTest.cpp
2930
)
3031

0 commit comments

Comments
 (0)