Skip to content

Commit f0204b6

Browse files
committed
Add --save-as param to record
`--save-as` specifies the trace dir name (basename?), not the full path. Merged `output_trace_dir` in RecordFlags with `name` into new type `TraceOutputPath`. Changed all usage of `output_trace_dir` string to use this type instead. This PR is backwards compatible, in the sense that the old usage of the `-o` flag remains the same, if `--save-as` is not provided, i.e., will error out, if dir exists etc. If `--save-as` is provided together with `-o` the new behavior will happen instead, where the output dir will be the "root dir", thus, the user can save many traces there. If only `--save-as` is provided, record uses normal behavior, of setting the trace root dir to $RR_TRACE_DIR (or it's user provided env var). Naming of user provided dirs, follows old behavior, i.e. appending -0, -1, -2 etc to the trace dir. I think this is preferable - if some automated thing is recording something with a specific name provided, this makes it so the end user don't have to manage the file system (i.e. checking if that name is "taken" and having to do clean up before recording, etc.) Added test cases
1 parent f7067f1 commit f0204b6

8 files changed

+97
-38
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1751,6 +1751,7 @@ set(TESTS_WITHOUT_PROGRAM
17511751
run_end
17521752
run_in_function
17531753
sanity
1754+
save_as
17541755
seekticks
17551756
shm_checkpoint
17561757
siginfo

src/RecordCommand.cc

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ RecordCommand RecordCommand::singleton(
6161
" _RR_TRACE_DIR gets ignored.\n"
6262
" Directory name is given name, not the\n"
6363
" application name.\n"
64+
" --save-as=<NAME> Name of the new recording's directory. If\n"
65+
" a recording with that name already exists, normal\n"
66+
" number appending is applied."
6467
" -p --print-trace-dir=<NUM> print trace directory followed by a newline\n"
6568
" to given file descriptor\n"
6669
" --syscall-buffer-sig=<NUM> the signal used for communication with the\n"
@@ -134,7 +137,7 @@ struct RecordFlags {
134137

135138
int print_trace_dir;
136139

137-
string output_trace_dir;
140+
TraceOutputPath path;
138141

139142
/* Whether to use file-cloning optimization during recording. */
140143
bool use_file_cloning;
@@ -200,7 +203,7 @@ struct RecordFlags {
200203
use_syscall_buffer(RecordSession::ENABLE_SYSCALL_BUF),
201204
syscall_buffer_size(0),
202205
print_trace_dir(-1),
203-
output_trace_dir(""),
206+
path{"", "", false, false},
204207
use_file_cloning(true),
205208
use_read_cloning(true),
206209
bind_cpu(BIND_CPU),
@@ -282,6 +285,7 @@ static bool parse_record_arg(vector<string>& args, RecordFlags& flags) {
282285
{ 18, "tsan", NO_PARAMETER },
283286
{ 19, "intel-pt", NO_PARAMETER },
284287
{ 20, "check-outside-mmaps", NO_PARAMETER },
288+
{ 21, "save-as", HAS_PARAMETER },
285289
{ 'c', "num-cpu-ticks", HAS_PARAMETER },
286290
{ 'h', "chaos", NO_PARAMETER },
287291
{ 'i', "ignore-signal", HAS_PARAMETER },
@@ -293,6 +297,7 @@ static bool parse_record_arg(vector<string>& args, RecordFlags& flags) {
293297
{ 'u', "cpu-unbound", NO_PARAMETER },
294298
{ 'v', "env", HAS_PARAMETER },
295299
{ 'w', "wait", NO_PARAMETER }};
300+
296301
ParsedOption opt;
297302
auto args_copy = args;
298303
if (!Command::parse_option(args_copy, options, &opt)) {
@@ -327,7 +332,8 @@ static bool parse_record_arg(vector<string>& args, RecordFlags& flags) {
327332
flags.print_trace_dir = opt.int_value;
328333
break;
329334
case 'o':
330-
flags.output_trace_dir = opt.value;
335+
flags.path.output_trace_dir = opt.value;
336+
flags.path.usr_provided_outdir = true;
331337
break;
332338
case 0:
333339
flags.use_read_cloning = false;
@@ -501,6 +507,10 @@ static bool parse_record_arg(vector<string>& args, RecordFlags& flags) {
501507
case 20:
502508
flags.check_outside_mmaps = true;
503509
break;
510+
case 21:
511+
flags.path.name = opt.value;
512+
flags.path.usr_provided_name = true;
513+
break;
504514
case 's':
505515
flags.always_switch = true;
506516
break;
@@ -684,11 +694,11 @@ static void* repeat_SIGTERM(__attribute__((unused)) void* p) {
684694

685695
static WaitStatus record(const vector<string>& args, const RecordFlags& flags) {
686696
LOG(info) << "Start recording...";
687-
697+
DEBUG_ASSERT(!flags.path.name.empty() && !flags.path.output_trace_dir.empty() && "No output dir or trace dir name set");
688698
auto session = RecordSession::create(
689-
args, flags.extra_env, flags.disable_cpuid_features,
699+
args, flags.extra_env, flags.disable_cpuid_features, flags.path,
690700
flags.use_syscall_buffer, flags.syscallbuf_desched_sig,
691-
flags.bind_cpu, flags.output_trace_dir,
701+
flags.bind_cpu,
692702
flags.trace_id.get(),
693703
flags.stap_sdt, flags.unmap_vdso, flags.asan, flags.tsan,
694704
flags.intel_pt, flags.check_outside_mmaps);
@@ -720,7 +730,7 @@ static WaitStatus record(const vector<string>& args, const RecordFlags& flags) {
720730
bool done_initial_exec = session->done_initial_exec();
721731
step_result = session->record_step();
722732
// Only create latest-trace symlink if --output-trace-dir is not being used
723-
if (!done_initial_exec && session->done_initial_exec() && flags.output_trace_dir.empty()) {
733+
if (!done_initial_exec && session->done_initial_exec() && !flags.path.usr_provided_outdir) {
724734
session->trace_writer().make_latest_trace();
725735
}
726736
if (term_requested) {
@@ -881,6 +891,16 @@ int RecordCommand::run(vector<string>& args) {
881891
flags.extra_env.push_back(padding);
882892
}
883893

894+
if(flags.path.name.empty()) {
895+
flags.path.name = args[0];
896+
flags.path.usr_provided_name = false;
897+
}
898+
899+
if(flags.path.output_trace_dir.empty()) {
900+
flags.path.usr_provided_outdir = false;
901+
flags.path.output_trace_dir = trace_save_dir();
902+
}
903+
884904
WaitStatus status = record(args, flags);
885905

886906
// Everything should have been cleaned up by now.

src/RecordSession.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2383,10 +2383,10 @@ static string lookup_by_path(const string& name) {
23832383
/*static*/ RecordSession::shr_ptr RecordSession::create(
23842384
const vector<string>& argv, const vector<string>& extra_env,
23852385
const DisableCPUIDFeatures& disable_cpuid_features,
2386+
const TraceOutputPath& path_info,
23862387
SyscallBuffering syscallbuf,
23872388
unsigned char syscallbuf_desched_sig,
23882389
BindCPU bind_cpu,
2389-
const string& output_trace_dir,
23902390
const TraceUuid* trace_id,
23912391
bool use_audit,
23922392
bool unmap_vdso,
@@ -2500,7 +2500,7 @@ static string lookup_by_path(const string& name) {
25002500
shr_ptr session(
25012501
new RecordSession(full_path, argv, env, disable_cpuid_features,
25022502
syscallbuf, syscallbuf_desched_sig, bind_cpu,
2503-
output_trace_dir, trace_id, use_audit, unmap_vdso,
2503+
path_info, trace_id, use_audit, unmap_vdso,
25042504
intel_pt, check_outside_mmaps));
25052505
session->excluded_ranges_ = std::move(exe_info.sanitizer_exclude_memory_ranges);
25062506
session->fixed_global_exclusion_range_ = std::move(exe_info.fixed_global_exclusion_range);
@@ -2514,13 +2514,13 @@ RecordSession::RecordSession(const std::string& exe_path,
25142514
SyscallBuffering syscallbuf,
25152515
int syscallbuf_desched_sig,
25162516
BindCPU bind_cpu,
2517-
const string& output_trace_dir,
2517+
const TraceOutputPath& path,
25182518
const TraceUuid* trace_id,
25192519
bool use_audit,
25202520
bool unmap_vdso,
25212521
bool intel_pt_enabled,
25222522
bool check_outside_mmaps)
2523-
: trace_out(argv[0], output_trace_dir, ticks_semantics_),
2523+
: trace_out(path, ticks_semantics_),
25242524
scheduler_(*this),
25252525
trace_id(trace_id),
25262526
disable_cpuid_features_(disable_cpuid_features),

src/RecordSession.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "Session.h"
1212
#include "ThreadGroup.h"
1313
#include "TraceFrame.h"
14+
#include "TraceStream.h"
1415
#include "WaitStatus.h"
1516

1617
namespace rr {
@@ -63,10 +64,10 @@ class RecordSession final : public Session {
6364
const std::vector<std::string>& argv,
6465
const std::vector<std::string>& extra_env,
6566
const DisableCPUIDFeatures& features,
67+
const TraceOutputPath& path_info,
6668
SyscallBuffering syscallbuf = ENABLE_SYSCALL_BUF,
6769
unsigned char syscallbuf_desched_sig = SIGPWR,
6870
BindCPU bind_cpu = BIND_CPU,
69-
const std::string& output_trace_dir = "",
7071
const TraceUuid* trace_id = nullptr,
7172
bool use_audit = false,
7273
bool unmap_vdso = false,
@@ -219,7 +220,7 @@ class RecordSession final : public Session {
219220
SyscallBuffering syscallbuf,
220221
int syscallbuf_desched_sig,
221222
BindCPU bind_cpu,
222-
const std::string& output_trace_dir,
223+
const TraceOutputPath& path_info,
223224
const TraceUuid* trace_id,
224225
bool use_audit,
225226
bool unmap_vdso,
@@ -285,8 +286,6 @@ class RecordSession final : public Session {
285286
*/
286287
std::map<pid_t, RecordTask*> detached_task_map;
287288

288-
std::string output_trace_dir;
289-
290289
bool use_audit_;
291290
bool unmap_vdso_;
292291
bool check_outside_mmaps_;

src/TraceStream.cc

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,31 +1314,16 @@ bool TraceReader::read_raw_data_metadata_for_frame(RawDataMetadata& d) {
13141314
return true;
13151315
}
13161316

1317-
static string make_trace_dir(const string& exe_path, const string& output_trace_dir) {
1318-
if (!output_trace_dir.empty()) {
1319-
// save trace dir in given output trace dir with option -o
1320-
int ret = mkdir(output_trace_dir.c_str(), S_IRWXU | S_IRWXG);
1321-
if (ret == 0) {
1322-
return output_trace_dir;
1323-
}
1324-
if (EEXIST == errno) {
1325-
CLEAN_FATAL() << "Trace directory `" << output_trace_dir << "' already exists.";
1326-
} else if (EACCES == errno) {
1327-
CLEAN_FATAL() << "Permission denied to create trace directory `" << output_trace_dir << "'";
1328-
} else {
1329-
FATAL() << "Unable to create trace directory `" << output_trace_dir << "'";
1330-
}
1331-
} else {
1332-
// save trace dir set in _RR_TRACE_DIR or in the default trace dir
1333-
ensure_dir(trace_save_dir(), "trace directory", S_IRWXU);
1317+
static string create_unique_dir(const TraceOutputPath& path) {
1318+
ensure_dir(path.output_trace_dir, "trace directory", S_IRWXU);
13341319

13351320
// Find a unique trace directory name.
13361321
int nonce = 0;
13371322
int ret;
13381323
string dir;
13391324
do {
13401325
stringstream ss;
1341-
ss << trace_save_dir() << "/" << basename(exe_path.c_str()) << "-"
1326+
ss << path.output_trace_dir << "/" << basename(path.name.c_str()) << "-"
13421327
<< nonce++;
13431328
dir = ss.str();
13441329
ret = mkdir(dir.c_str(), S_IRWXU | S_IRWXG);
@@ -1349,6 +1334,28 @@ static string make_trace_dir(const string& exe_path, const string& output_trace_
13491334
}
13501335

13511336
return dir;
1337+
}
1338+
1339+
static string make_trace_dir(const TraceOutputPath& path) {
1340+
if (path.usr_provided_outdir) {
1341+
// save trace dir in given output trace dir with option -o
1342+
int ret = mkdir(path.output_trace_dir.c_str(), S_IRWXU | S_IRWXG);
1343+
if(path.usr_provided_name) {
1344+
return create_unique_dir(path);
1345+
}
1346+
if (ret == 0) {
1347+
return path.output_trace_dir;
1348+
}
1349+
if (EEXIST == errno) {
1350+
CLEAN_FATAL() << "Trace directory `" << path.output_trace_dir << "' already exists.";
1351+
} else if (EACCES == errno) {
1352+
CLEAN_FATAL() << "Permission denied to create trace directory `" << path.output_trace_dir << "'";
1353+
} else {
1354+
FATAL() << "Unable to create trace directory `" << path.output_trace_dir << "'";
1355+
}
1356+
} else {
1357+
// save trace dir set in _RR_TRACE_DIR or in the default trace dir
1358+
return create_unique_dir(path);
13521359
}
13531360

13541361
return ""; // not reached
@@ -1357,10 +1364,9 @@ static string make_trace_dir(const string& exe_path, const string& output_trace_
13571364
#define STR_HELPER(x) #x
13581365
#define STR(x) STR_HELPER(x)
13591366

1360-
TraceWriter::TraceWriter(const std::string& file_name,
1361-
const string& output_trace_dir,
1367+
TraceWriter::TraceWriter(const TraceOutputPath& trace_output,
13621368
TicksSemantics ticks_semantics_)
1363-
: TraceStream(make_trace_dir(file_name, output_trace_dir),
1369+
: TraceStream(make_trace_dir(trace_output),
13641370
// Somewhat arbitrarily start the
13651371
// global time from 1.
13661372
1),

src/TraceStream.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "TraceFrame.h"
2020
#include "TraceTaskEvent.h"
2121
#include "remote_ptr.h"
22+
#include "util.h"
2223

2324
namespace rr {
2425

@@ -262,8 +263,7 @@ class TraceWriter : public TraceStream {
262263
* The trace name is determined by |file_name| and _RR_TRACE_DIR (if set)
263264
* or by setting -o=<OUTPUT_TRACE_DIR>.
264265
*/
265-
TraceWriter(const std::string& file_name,
266-
const string& output_trace_dir, TicksSemantics ticks_semantics);
266+
TraceWriter(const TraceOutputPath& path, TicksSemantics ticks_semantics);
267267

268268
/**
269269
* Called after the calling thread is actually bound to |bind_to_cpu|.

src/test/save_as.run

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
source `dirname $0`/util.sh
2+
3+
function dir_exists { checkDir=$1;
4+
if [ -d "$checkDir" ]; then
5+
echo "$checkDir" exists
6+
else
7+
failed "$checkDir doesn't exist"
8+
exit 1
9+
fi
10+
}
11+
12+
RECORD_ARGS="--save-as test-name"
13+
record hello
14+
dir_exists "$workdir/test-name-0"
15+
16+
record hello
17+
dir_exists "$workdir/test-name-1"
18+
19+
20+
# Check that --save-as plays nice with -o <trace dir>
21+
RECORD_ARGS="-o $workdir/arbitrary-dir --save-as test-name"
22+
record hello
23+
dir_exists "$workdir/arbitrary-dir/test-name-0"
24+
record hello
25+
dir_exists "$workdir/arbitrary-dir/test-name-1"
26+
27+
passed_msg "--save-as and --save-as -o combination succeeded"

src/util.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,12 @@ void replace_in_buffer(MemoryRange src, const uint8_t* src_data,
696696

697697
// Strip any directory part from the filename `s`
698698
void base_name(std::string& s);
699+
struct TraceOutputPath {
700+
std::string output_trace_dir;
701+
std::string name;
702+
bool usr_provided_outdir;
703+
bool usr_provided_name;
704+
};
699705

700706
std::optional<int> read_perf_event_paranoid();
701707

0 commit comments

Comments
 (0)