LinuxArmSystem::startup()
{
if (enableContextSwitchStatsDump) {
- dumpStatsPCEvent = addKernelFuncEvent<DumpStatsPCEvent>("__switch_to");
+ if (!highestELIs64()) {
+ dumpStatsPCEvent =
+ addKernelFuncEvent<DumpStatsPCEvent>("__switch_to");
+ } else {
+ dumpStatsPCEvent =
+ addKernelFuncEvent<DumpStatsPCEvent64>("__switch_to");
+ }
+
if (!dumpStatsPCEvent)
panic("dumpStatsPCEvent not created!");
Linux::dumpDmesg(getThreadContext(0), std::cout);
}
-/** This function is called whenever the the kernel function
- * "__switch_to" is called to change running tasks.
+/**
+ * Extracts the information used by the DumpStatsPCEvent by reading the
+ * thread_info pointer passed to __switch_to() in 32 bit ARM Linux
*
* r0 = task_struct of the previously running process
- * r1 = task_info of the previously running process
- * r2 = task_info of the next process to run
+ * r1 = thread_info of the previously running process
+ * r2 = thread_info of the next process to run
*/
void
-DumpStatsPCEvent::process(ThreadContext *tc)
-{
+DumpStatsPCEvent::getTaskDetails(ThreadContext *tc, uint32_t &pid,
+ uint32_t &tgid, std::string &next_task_str, int32_t &mm) {
+
Linux::ThreadInfo ti(tc);
Addr task_descriptor = tc->readIntReg(2);
- uint32_t pid = ti.curTaskPID(task_descriptor);
- uint32_t tgid = ti.curTaskTGID(task_descriptor);
- std::string next_task_str = ti.curTaskName(task_descriptor);
+ pid = ti.curTaskPID(task_descriptor);
+ tgid = ti.curTaskTGID(task_descriptor);
+ next_task_str = ti.curTaskName(task_descriptor);
// Streamline treats pid == -1 as the kernel process.
// Also pid == 0 implies idle process (except during Linux boot)
- int32_t mm = ti.curTaskMm(task_descriptor);
+ mm = ti.curTaskMm(task_descriptor);
+}
+
+/**
+ * Extracts the information used by the DumpStatsPCEvent64 by reading the
+ * task_struct pointer passed to __switch_to() in 64 bit ARM Linux
+ *
+ * r0 = task_struct of the previously running process
+ * r1 = task_struct of next process to run
+ */
+void
+DumpStatsPCEvent64::getTaskDetails(ThreadContext *tc, uint32_t &pid,
+ uint32_t &tgid, std::string &next_task_str, int32_t &mm) {
+
+ Linux::ThreadInfo ti(tc);
+ Addr task_struct = tc->readIntReg(1);
+ pid = ti.curTaskPIDFromTaskStruct(task_struct);
+ tgid = ti.curTaskTGIDFromTaskStruct(task_struct);
+ next_task_str = ti.curTaskNameFromTaskStruct(task_struct);
+
+ // Streamline treats pid == -1 as the kernel process.
+ // Also pid == 0 implies idle process (except during Linux boot)
+ mm = ti.curTaskMmFromTaskStruct(task_struct);
+}
+
+/** This function is called whenever the the kernel function
+ * "__switch_to" is called to change running tasks.
+ */
+void
+DumpStatsPCEvent::process(ThreadContext *tc)
+{
+ uint32_t pid = 0;
+ uint32_t tgid = 0;
+ std::string next_task_str;
+ int32_t mm = 0;
+
+ getTaskDetails(tc, pid, tgid, next_task_str, mm);
+
bool is_kernel = (mm == 0);
if (is_kernel && (pid != 0)) {
pid = -1;
{}
virtual void process(ThreadContext* tc);
+ protected:
+ virtual void getTaskDetails(ThreadContext *tc, uint32_t &pid,
+ uint32_t &tgid, std::string &next_task_str, int32_t &mm);
+
+};
+
+class DumpStatsPCEvent64 : public DumpStatsPCEvent {
+ public:
+ DumpStatsPCEvent64(PCEventQueue *q, const std::string &desc, Addr addr)
+ : DumpStatsPCEvent(q, desc, addr)
+ {}
+ private:
+ void getTaskDetails(ThreadContext *tc, uint32_t &pid, uint32_t &tgid,
+ std::string &next_task_str, int32_t &mm) override;
};
inline Addr
curTaskInfo(Addr thread_info = 0)
{
+ // Note that in Linux 4.10 the thread_info struct will no longer have a
+ // pointer to the task_struct for arm64. See:
+ // https://patchwork.kernel.org/patch/9333699/
int32_t offset;
if (!get_data("thread_info_task", offset))
return 0;
}
int32_t
- curTaskPID(Addr thread_info = 0)
- {
+ curTaskPIDFromTaskStruct(Addr task_struct) {
int32_t offset;
if (!get_data("task_struct_pid", offset))
return -1;
int32_t pid;
- CopyOut(tc, &pid, curTaskInfo(thread_info) + offset, sizeof(pid));
+ CopyOut(tc, &pid, task_struct + offset, sizeof(pid));
return pid;
}
int32_t
- curTaskTGID(Addr thread_info = 0)
+ curTaskPID(Addr thread_info = 0)
+ {
+ return curTaskPIDFromTaskStruct(curTaskInfo(thread_info));
+ }
+
+ int32_t
+ curTaskTGIDFromTaskStruct(Addr task_struct)
{
int32_t offset;
if (!get_data("task_struct_tgid", offset))
return -1;
int32_t tgid;
- CopyOut(tc, &tgid, curTaskInfo(thread_info) + offset, sizeof(tgid));
+ CopyOut(tc, &tgid, task_struct + offset, sizeof(tgid));
return tgid;
}
+ int32_t
+ curTaskTGID(Addr thread_info = 0)
+ {
+ return curTaskTGIDFromTaskStruct(curTaskInfo(thread_info));
+ }
+
int64_t
- curTaskStart(Addr thread_info = 0)
+ curTaskStartFromTaskStruct(Addr task_struct)
{
int32_t offset;
if (!get_data("task_struct_start_time", offset))
int64_t data;
// start_time is actually of type timespec, but if we just
// grab the first long, we'll get the seconds out of it
- CopyOut(tc, &data, curTaskInfo(thread_info) + offset, sizeof(data));
+ CopyOut(tc, &data, task_struct + offset, sizeof(data));
return data;
}
+ int64_t
+ curTaskStart(Addr thread_info = 0)
+ {
+ return curTaskStartFromTaskStruct(curTaskInfo(thread_info));
+ }
+
std::string
- curTaskName(Addr thread_info = 0)
+ curTaskNameFromTaskStruct(Addr task_struct)
{
int32_t offset;
int32_t size;
return "FailureIn_curTaskName";
char buffer[size + 1];
- CopyStringOut(tc, buffer, curTaskInfo(thread_info) + offset, size);
+ CopyStringOut(tc, buffer, task_struct + offset, size);
return buffer;
}
+ std::string
+ curTaskName(Addr thread_info = 0)
+ {
+ return curTaskNameFromTaskStruct(curTaskInfo(thread_info));
+ }
+
int32_t
- curTaskMm(Addr thread_info = 0)
+ curTaskMmFromTaskStruct(Addr task_struct)
{
int32_t offset;
if (!get_data("task_struct_mm", offset))
return -1;
int32_t mm_ptr;
- CopyOut(tc, &mm_ptr, curTaskInfo(thread_info) + offset, sizeof(mm_ptr));
+ CopyOut(tc, &mm_ptr, task_struct + offset, sizeof(mm_ptr));
return mm_ptr;
}
+
+ int32_t
+ curTaskMm(Addr thread_info = 0)
+ {
+ return curTaskMmFromTaskStruct(curTaskInfo(thread_info));
+ }
};
} // namespace Linux