Skip to content

Commit c841941

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 556551e commit c841941

8 files changed

+97
-38
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1730,6 +1730,7 @@ set(TESTS_WITHOUT_PROGRAM
17301730
run_end
17311731
run_in_function
17321732
sanity
1733+
save_as
17331734
seekticks
17341735
shm_checkpoint
17351736
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"
@@ -132,7 +135,7 @@ struct RecordFlags {
132135

133136
int print_trace_dir;
134137

135-
string output_trace_dir;
138+
TraceOutputPath path;
136139

137140
/* Whether to use file-cloning optimization during recording. */
138141
bool use_file_cloning;
@@ -195,7 +198,7 @@ struct RecordFlags {
195198
use_syscall_buffer(RecordSession::ENABLE_SYSCALL_BUF),
196199
syscall_buffer_size(0),
197200
print_trace_dir(-1),
198-
output_trace_dir(""),
201+
path{"", "", false, false},
199202
use_file_cloning(true),
200203
use_read_cloning(true),
201204
bind_cpu(BIND_CPU),
@@ -275,6 +278,7 @@ static bool parse_record_arg(vector<string>& args, RecordFlags& flags) {
275278
{ 17, "asan", NO_PARAMETER },
276279
{ 18, "tsan", NO_PARAMETER },
277280
{ 19, "intel-pt", NO_PARAMETER },
281+
{ 20, "save-as", HAS_PARAMETER },
278282
{ 'c', "num-cpu-ticks", HAS_PARAMETER },
279283
{ 'h', "chaos", NO_PARAMETER },
280284
{ 'i', "ignore-signal", HAS_PARAMETER },
@@ -286,6 +290,7 @@ static bool parse_record_arg(vector<string>& args, RecordFlags& flags) {
286290
{ 'u', "cpu-unbound", NO_PARAMETER },
287291
{ 'v', "env", HAS_PARAMETER },
288292
{ 'w', "wait", NO_PARAMETER }};
293+
289294
ParsedOption opt;
290295
auto args_copy = args;
291296
if (!Command::parse_option(args_copy, options, &opt)) {
@@ -320,7 +325,8 @@ static bool parse_record_arg(vector<string>& args, RecordFlags& flags) {
320325
flags.print_trace_dir = opt.int_value;
321326
break;
322327
case 'o':
323-
flags.output_trace_dir = opt.value;
328+
flags.path.output_trace_dir = opt.value;
329+
flags.path.usr_provided_outdir = true;
324330
break;
325331
case 0:
326332
flags.use_read_cloning = false;
@@ -491,6 +497,10 @@ static bool parse_record_arg(vector<string>& args, RecordFlags& flags) {
491497
case 19:
492498
flags.intel_pt = true;
493499
break;
500+
case 20:
501+
flags.path.name = opt.value;
502+
flags.path.usr_provided_name = true;
503+
break;
494504
case 's':
495505
flags.always_switch = true;
496506
break;
@@ -674,11 +684,11 @@ static void* repeat_SIGTERM(__attribute__((unused)) void* p) {
674684

675685
static WaitStatus record(const vector<string>& args, const RecordFlags& flags) {
676686
LOG(info) << "Start recording...";
677-
687+
DEBUG_ASSERT(!flags.path.name.empty() && !flags.path.output_trace_dir.empty() && "No output dir or trace dir name set");
678688
auto session = RecordSession::create(
679-
args, flags.extra_env, flags.disable_cpuid_features,
689+
args, flags.extra_env, flags.disable_cpuid_features, flags.path,
680690
flags.use_syscall_buffer, flags.syscallbuf_desched_sig,
681-
flags.bind_cpu, flags.output_trace_dir,
691+
flags.bind_cpu,
682692
flags.trace_id.get(),
683693
flags.stap_sdt, flags.unmap_vdso, flags.asan, flags.tsan,
684694
flags.intel_pt);
@@ -710,7 +720,7 @@ static WaitStatus record(const vector<string>& args, const RecordFlags& flags) {
710720
bool done_initial_exec = session->done_initial_exec();
711721
step_result = session->record_step();
712722
// Only create latest-trace symlink if --output-trace-dir is not being used
713-
if (!done_initial_exec && session->done_initial_exec() && flags.output_trace_dir.empty()) {
723+
if (!done_initial_exec && session->done_initial_exec() && !flags.path.usr_provided_outdir) {
714724
session->trace_writer().make_latest_trace();
715725
}
716726
if (term_requested) {
@@ -871,6 +881,16 @@ int RecordCommand::run(vector<string>& args) {
871881
flags.extra_env.push_back(padding);
872882
}
873883

884+
if(flags.path.name.empty()) {
885+
flags.path.name = args[0];
886+
flags.path.usr_provided_name = false;
887+
}
888+
889+
if(flags.path.output_trace_dir.empty()) {
890+
flags.path.usr_provided_outdir = false;
891+
flags.path.output_trace_dir = trace_save_dir();
892+
}
893+
874894
WaitStatus status = record(args, flags);
875895

876896
// 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
@@ -2375,10 +2375,10 @@ static string lookup_by_path(const string& name) {
23752375
/*static*/ RecordSession::shr_ptr RecordSession::create(
23762376
const vector<string>& argv, const vector<string>& extra_env,
23772377
const DisableCPUIDFeatures& disable_cpuid_features,
2378+
const TraceOutputPath& path_info,
23782379
SyscallBuffering syscallbuf,
23792380
unsigned char syscallbuf_desched_sig,
23802381
BindCPU bind_cpu,
2381-
const string& output_trace_dir,
23822382
const TraceUuid* trace_id,
23832383
bool use_audit,
23842384
bool unmap_vdso,
@@ -2514,7 +2514,7 @@ static string lookup_by_path(const string& name) {
25142514
shr_ptr session(
25152515
new RecordSession(full_path, argv, env, disable_cpuid_features,
25162516
syscallbuf, syscallbuf_desched_sig, bind_cpu,
2517-
output_trace_dir, trace_id, use_audit, unmap_vdso,
2517+
path_info, trace_id, use_audit, unmap_vdso,
25182518
intel_pt));
25192519
session->excluded_ranges_ = std::move(exe_info.sanitizer_exclude_memory_ranges);
25202520
session->fixed_global_exclusion_range_ = std::move(exe_info.fixed_global_exclusion_range);
@@ -2528,12 +2528,12 @@ RecordSession::RecordSession(const std::string& exe_path,
25282528
SyscallBuffering syscallbuf,
25292529
int syscallbuf_desched_sig,
25302530
BindCPU bind_cpu,
2531-
const string& output_trace_dir,
2531+
const TraceOutputPath& path,
25322532
const TraceUuid* trace_id,
25332533
bool use_audit,
25342534
bool unmap_vdso,
25352535
bool intel_pt_enabled)
2536-
: trace_out(argv[0], output_trace_dir, ticks_semantics_),
2536+
: trace_out(path, ticks_semantics_),
25372537
scheduler_(*this),
25382538
trace_id(trace_id),
25392539
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,
@@ -217,7 +218,7 @@ class RecordSession final : public Session {
217218
SyscallBuffering syscallbuf,
218219
int syscallbuf_desched_sig,
219220
BindCPU bind_cpu,
220-
const std::string& output_trace_dir,
221+
const TraceOutputPath& path_info,
221222
const TraceUuid* trace_id,
222223
bool use_audit,
223224
bool unmap_vdso,
@@ -282,8 +283,6 @@ class RecordSession final : public Session {
282283
*/
283284
std::map<pid_t, RecordTask*> detached_task_map;
284285

285-
std::string output_trace_dir;
286-
287286
bool use_audit_;
288287
bool unmap_vdso_;
289288
};

src/TraceStream.cc

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,31 +1309,16 @@ bool TraceReader::read_raw_data_metadata_for_frame(RawDataMetadata& d) {
13091309
return true;
13101310
}
13111311

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

13301315
// Find a unique trace directory name.
13311316
int nonce = 0;
13321317
int ret;
13331318
string dir;
13341319
do {
13351320
stringstream ss;
1336-
ss << trace_save_dir() << "/" << basename(exe_path.c_str()) << "-"
1321+
ss << path.output_trace_dir << "/" << basename(path.name.c_str()) << "-"
13371322
<< nonce++;
13381323
dir = ss.str();
13391324
ret = mkdir(dir.c_str(), S_IRWXU | S_IRWXG);
@@ -1344,6 +1329,28 @@ static string make_trace_dir(const string& exe_path, const string& output_trace_
13441329
}
13451330

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

13491356
return ""; // not reached
@@ -1352,10 +1359,9 @@ static string make_trace_dir(const string& exe_path, const string& output_trace_
13521359
#define STR_HELPER(x) #x
13531360
#define STR(x) STR_HELPER(x)
13541361

1355-
TraceWriter::TraceWriter(const std::string& file_name,
1356-
const string& output_trace_dir,
1362+
TraceWriter::TraceWriter(const TraceOutputPath& trace_output,
13571363
TicksSemantics ticks_semantics_)
1358-
: TraceStream(make_trace_dir(file_name, output_trace_dir),
1364+
: TraceStream(make_trace_dir(trace_output),
13591365
// Somewhat arbitrarily start the
13601366
// global time from 1.
13611367
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

@@ -249,8 +250,7 @@ class TraceWriter : public TraceStream {
249250
* The trace name is determined by |file_name| and _RR_TRACE_DIR (if set)
250251
* or by setting -o=<OUTPUT_TRACE_DIR>.
251252
*/
252-
TraceWriter(const std::string& file_name,
253-
const string& output_trace_dir, TicksSemantics ticks_semantics);
253+
TraceWriter(const TraceOutputPath& path, TicksSemantics ticks_semantics);
254254

255255
/**
256256
* 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
@@ -663,6 +663,12 @@ void replace_in_buffer(MemoryRange src, const uint8_t* src_data,
663663

664664
// Strip any directory part from the filename `s`
665665
void base_name(std::string& s);
666+
struct TraceOutputPath {
667+
std::string output_trace_dir;
668+
std::string name;
669+
bool usr_provided_outdir;
670+
bool usr_provided_name;
671+
};
666672

667673
} // namespace rr
668674

0 commit comments

Comments
 (0)