Permalink
Browse files

Virtualize ticks perf event in recorded tasks.

  • Loading branch information...
rocallahan committed Apr 6, 2016
1 parent 5512b1b commit 987b8f54306833b766383a03059738d0bc4a432c
View
@@ -243,6 +243,7 @@ add_executable(rr
src/TaskGroup.cc
src/TraceFrame.cc
src/TraceStream.cc
+ src/VirtualPerfCounterMonitor.cc
src/util.cc
src/WaitStatus.cc
)
View
@@ -32,6 +32,32 @@ bool FdTable::allow_close(int fd) {
return it->second->allow_close();
}
+bool FdTable::emulate_ioctl(int fd, RecordTask* t, uint64_t* result) {
+ auto it = fds.find(fd);
+ if (it == fds.end()) {
+ return false;
+ }
+ return it->second->emulate_ioctl(t, result);
+}
+
+bool FdTable::emulate_fcntl(int fd, RecordTask* t, uint64_t* result) {
+ auto it = fds.find(fd);
+ if (it == fds.end()) {
+ return false;
+ }
+ return it->second->emulate_fcntl(t, result);
+}
+
+bool FdTable::emulate_read(int fd, RecordTask* t,
+ const std::vector<FileMonitor::Range>& ranges,
+ off_t offset, uint64_t* result) {
+ auto it = fds.find(fd);
+ if (it == fds.end()) {
+ return false;
+ }
+ return it->second->emulate_read(t, ranges, offset, result);
+}
+
Switchable FdTable::will_write(Task* t, int fd) {
auto it = fds.find(fd);
if (it == fds.end()) {
View
@@ -18,6 +18,11 @@ class FdTable : public HasTaskSet {
typedef std::shared_ptr<FdTable> shr_ptr;
void add_monitor(int fd, FileMonitor* monitor);
+ bool emulate_ioctl(int fd, RecordTask* t, uint64_t* result);
+ bool emulate_fcntl(int fd, RecordTask* t, uint64_t* result);
+ bool emulate_read(int fd, RecordTask* t,
+ const std::vector<FileMonitor::Range>& ranges, off_t offset,
+ uint64_t* result);
bool allow_close(int fd);
Switchable will_write(Task* t, int fd);
void did_write(Task* t, int fd, const std::vector<FileMonitor::Range>& ranges,
View
@@ -15,6 +15,9 @@ class Task;
namespace rr {
+class RecordTask;
+class Registers;
+
class FileMonitor {
public:
typedef std::shared_ptr<FileMonitor> shr_ptr;
@@ -50,6 +53,31 @@ class FileMonitor {
Range(remote_ptr<void> data, size_t length) : data(data), length(length) {}
};
virtual void did_write(Task*, const std::vector<Range>&, off_t) {}
+
+ /**
+ * Return true if the ioctl should be fully emulated. If so the result
+ * is stored in the last parameter.
+ * Only called during recording.
+ */
+ virtual bool emulate_ioctl(RecordTask*, uint64_t*) { return false; }
+
+ /**
+ * Return true if the fcntl should should be fully emulated. If so the
+ * result is stored in the last parameter.
+ * Only called during recording.
+ */
+ virtual bool emulate_fcntl(RecordTask*, uint64_t*) { return false; }
+
+ /**
+ * Return true if the read should should be fully emulated. If so the
+ * result is stored in the last parameter. The emulation should write to the
+ * task's memory ranges.
+ * Only called during recording.
+ */
+ virtual bool emulate_read(RecordTask*, const std::vector<Range>&, off_t,
+ uint64_t*) {
+ return false;
+ }
};
} // namespace rr
View
@@ -183,6 +183,11 @@ static void init_attributes() {
PERF_COUNT_SW_PAGE_FAULTS);
}
+const struct perf_event_attr& PerfCounters::ticks_attr() {
+ init_attributes();
+ return rr::ticks_attr;
+}
+
PerfCounters::PerfCounters(pid_t tid) : tid(tid), started(false) {
init_attributes();
}
@@ -212,7 +217,7 @@ void PerfCounters::reset(Ticks ticks_period) {
stop();
- struct perf_event_attr attr = ticks_attr;
+ struct perf_event_attr attr = rr::ticks_attr;
attr.sample_period = ticks_period;
fd_ticks = start_counter(tid, -1, &attr);
View
@@ -14,6 +14,8 @@
#include "ScopedFd.h"
#include "Ticks.h"
+struct perf_event_attr;
+
namespace rr {
/**
@@ -78,6 +80,8 @@ class PerfCounters {
};
Extra read_extra();
+ static const struct perf_event_attr& ticks_attr();
+
private:
pid_t tid;
ScopedFd fd_ticks;
View
@@ -1567,4 +1567,8 @@ RecordTask* RecordSession::find_task(pid_t rec_tid) const {
return static_cast<RecordTask*>(Session::find_task(rec_tid));
}
+RecordTask* RecordSession::find_task(const TaskUid& tuid) const {
+ return static_cast<RecordTask*>(Session::find_task(tuid));
+}
+
} // namespace rr
View
@@ -106,6 +106,7 @@ class RecordSession : public Session {
SupportedArch a);
RecordTask* find_task(pid_t rec_tid) const;
+ RecordTask* find_task(const TaskUid& tuid) const;
private:
RecordSession(const std::vector<std::string>& argv,
View
@@ -839,12 +839,14 @@ void ReplaySession::prepare_syscallbuf_records(ReplayTask* t) {
<< " bytes of syscall records";
}
-static void apply_mprotect_records(ReplayTask* t, uint32_t skip_mprotect_records) {
+static void apply_mprotect_records(ReplayTask* t,
+ uint32_t skip_mprotect_records) {
uint32_t final_mprotect_record_count =
t->syscallbuf_hdr->mprotect_record_count;
if (skip_mprotect_records < final_mprotect_record_count) {
- auto records = t->read_mem(t->mprotect_records + skip_mprotect_records,
- final_mprotect_record_count - skip_mprotect_records);
+ auto records =
+ t->read_mem(t->mprotect_records + skip_mprotect_records,
+ final_mprotect_record_count - skip_mprotect_records);
for (size_t i = 0; i < records.size(); ++i) {
auto& r = records[i];
if (i >= t->syscallbuf_hdr->mprotect_record_count_completed) {
View
@@ -301,6 +301,26 @@ static vector<uint8_t> ptrace_get_regs_set(Task* t, const Registers& regs,
return t->read_mem(iov.iov_base.rptr().template cast<uint8_t>(), iov.iov_len);
}
+template <typename Arch>
+static off_t get_io_offset_arch(int syscallno, const Registers& regs) {
+ switch (syscallno) {
+ case Arch::pwrite64:
+ case Arch::pwritev:
+ case Arch::pread64:
+ case Arch::preadv:
+ if (sizeof(typename Arch::unsigned_word) == 4) {
+ return regs.arg4_signed() | (off_t(regs.arg5_signed()) << 32);
+ }
+ return regs.arg4_signed();
+ default:
+ return -1;
+ }
+}
+
+off_t Task::get_io_offset(int syscallno, const Registers& regs) {
+ RR_ARCH_FUNCTION(get_io_offset_arch, arch(), syscallno, regs);
+}
+
template <typename Arch>
void Task::on_syscall_exit_arch(int syscallno, const Registers& regs) {
session().accumulate_syscall_performed();
@@ -406,17 +426,8 @@ void Task::on_syscall_exit_arch(int syscallno, const Registers& regs) {
if (amount > 0) {
ranges.push_back(FileMonitor::Range(regs.arg2(), amount));
}
- off_t offset;
- if (syscallno == Arch::pwrite64) {
- if (sizeof(typename Arch::unsigned_word) == 4) {
- offset = regs.arg4_signed() | (off_t(regs.arg5_signed()) << 32);
- } else {
- offset = regs.arg4_signed();
- }
- } else {
- offset = -1;
- }
- fd_table()->did_write(this, fd, ranges, offset);
+ fd_table()->did_write(this, fd, ranges,
+ get_io_offset_arch<Arch>(syscallno, regs));
return;
}
@@ -435,17 +446,8 @@ void Task::on_syscall_exit_arch(int syscallno, const Registers& regs) {
written -= amount;
}
}
- off_t offset;
- if (syscallno == Arch::pwritev) {
- if (sizeof(typename Arch::unsigned_word) == 4) {
- offset = regs.arg4_signed() | (off_t(regs.arg5_signed()) << 32);
- } else {
- offset = regs.arg4_signed();
- }
- } else {
- offset = -1;
- }
- fd_table()->did_write(this, fd, ranges, offset);
+ fd_table()->did_write(this, fd, ranges,
+ get_io_offset_arch<Arch>(syscallno, regs));
return;
}
View
@@ -507,6 +507,12 @@ class Task {
*/
void reset_syscallbuf();
+ /**
+ * Compute the offset used by a read/write syscall. Returns -1 if the
+ * syscall doesn't pass an offset.
+ */
+ off_t get_io_offset(int syscallno, const Registers& regs);
+
/**
* Return the virtual memory mapping (address space) of this
* task.
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
+
+#include "VirtualPerfCounterMonitor.h"
+
+#include <linux/perf_event.h>
+#include <stdlib.h>
+
+#include "AutoRemoteSyscalls.h"
+#include "log.h"
+#include "RecordSession.h"
+#include "RecordTask.h"
+
+using namespace std;
+
+namespace rr {
+
+bool VirtualPerfCounterMonitor::should_virtualize(
+ const struct perf_event_attr& attr) {
+ auto ticks_attr = PerfCounters::ticks_attr();
+ ticks_attr.sample_period = attr.sample_period;
+ return !memcmp(&ticks_attr, &attr, sizeof(attr));
+}
+
+VirtualPerfCounterMonitor::VirtualPerfCounterMonitor(
+ Task* t, Task* target, const struct perf_event_attr& attr)
+ : initial_ticks(target->tick_count()),
+ target_tuid(target->tuid()),
+ owner_tid(0),
+ flags(0),
+ sig(0),
+ enabled(false) {
+ ASSERT(t, should_virtualize(attr));
+ // XXX When we support interrupts, we need to add code for signal dispatching
+ ASSERT(t, attr.sample_period == 0xffffffff) << "Don't support interrupts yet";
+}
+
+bool VirtualPerfCounterMonitor::emulate_ioctl(RecordTask* t, uint64_t* result) {
+ switch ((int)t->regs().arg2()) {
+ case PERF_EVENT_IOC_ENABLE:
+ *result = 0;
+ enabled = true;
+ break;
+ default:
+ ASSERT(t, false) << "Unsupported perf event ioctl "
+ << HEX((int)t->regs().arg2());
+ break;
+ }
+ return true;
+}
+
+bool VirtualPerfCounterMonitor::emulate_fcntl(RecordTask* t, uint64_t* result) {
+ *result = -(int64_t)EINVAL;
+ switch ((int)t->regs().arg2()) {
+ case F_SETOWN_EX: {
+ auto owner = t->read_mem(remote_ptr<struct f_owner_ex>(t->regs().arg3()));
+ ASSERT(t, owner.type == F_OWNER_TID)
+ << "Unsupported perf event F_SETOWN_EX type " << owner.type;
+ owner_tid = owner.pid;
+ *result = 0;
+ break;
+ }
+ case F_SETFL:
+ ASSERT(t, !(t->regs().arg3() & ~O_ASYNC))
+ << "Unsupported perf event flags " << HEX((int)t->regs().arg3());
+ flags = (int)t->regs().arg3();
+ *result = 0;
+ break;
+ case F_SETSIG:
+ sig = (int)t->regs().arg3();
+ *result = 0;
+ break;
+ default:
+ ASSERT(t, false) << "Unsupported perf event fnctl "
+ << HEX((int)t->regs().arg2());
+ break;
+ }
+ return true;
+}
+
+static size_t write_ranges(RecordTask* t,
+ const vector<FileMonitor::Range>& ranges, void* data,
+ size_t size) {
+ uint8_t* p = static_cast<uint8_t*>(data);
+ size_t s = size;
+ size_t result = 0;
+ for (auto& r : ranges) {
+ size_t bytes = min(s, r.length);
+ t->write_bytes_helper(r.data, bytes, p);
+ s -= bytes;
+ result += bytes;
+ }
+ return result;
+}
+
+bool VirtualPerfCounterMonitor::emulate_read(RecordTask* t,
+ const vector<Range>& ranges,
+ off_t, uint64_t* result) {
+ RecordTask* target = t->session().find_task(target_tuid);
+ if (target) {
+ int64_t val = target->tick_count() - initial_ticks;
+ *result = write_ranges(t, ranges, &val, sizeof(val));
+ } else {
+ *result = 0;
+ }
+ return true;
+}
+
+} // namespace rr
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
+
+#ifndef RR_VIRTUAL_PERF_COUNTER_MONITOR_H_
+#define RR_VIRTUAL_PERF_COUNTER_MONITOR_H_
+
+#include "FileMonitor.h"
+#include "TaskishUid.h"
+
+struct perf_event_attr;
+
+namespace rr {
+
+/**
+ * A FileMonitor to
+ */
+class VirtualPerfCounterMonitor : public FileMonitor {
+public:
+ static bool should_virtualize(const struct perf_event_attr& attr);
+
+ VirtualPerfCounterMonitor(Task* t, Task* target,
+ const struct perf_event_attr& attr);
+
+ virtual bool emulate_ioctl(RecordTask* t, uint64_t* result);
+ virtual bool emulate_fcntl(RecordTask* t, uint64_t* result);
+ virtual bool emulate_read(RecordTask* t, const std::vector<Range>& ranges,
+ off_t offset, uint64_t* result);
+
+private:
+ Ticks initial_ticks;
+ TaskUid target_tuid;
+ pid_t owner_tid;
+ int flags;
+ int sig;
+ bool enabled;
+};
+
+} // namespace rr
+
+#endif /* RR_VIRTUAL_PERF_COUNTER_MONITOR_H_ */
Oops, something went wrong.

0 comments on commit 987b8f5

Please sign in to comment.