From 1d7ff84f126e5e1b138c7250a275ebe3a46fa27c Mon Sep 17 00:00:00 2001 From: Paul Rosenfeld Date: Tue, 4 Apr 2017 09:06:38 -0600 Subject: arm,sim: fix context switch stats dumps for ARM64/Linux 32bit and 64bit Linux have different arguments passed to the __switch_to() function that gem5 hooks into in order to collect context switch statistics. 64bit Linux provides the task_struct pointer to the next task that will be switched to, which means we don't have to look up the task_struct from thread_info as we do in 32bit ARM Linux. This patch adds a second set of accessors to ThreadInfo to extract details such as the pid, tgid, task name, etc., directly from a task_struct. The existing accessors maintain their existing behavior by first looking up the task_struct and then calling these new accessors. A 64-bit variant of the DumpStatsPCEvent class is added that uses these new accessors to get the task details for the context switch dumps directly from the task_struct passed to __switch_to(). Change-Id: I63c4b3e1ad64446751a91f6340901d5180d7382d Reviewed-on: https://gem5-review.googlesource.com/2640 Reviewed-by: Curtis Dunham Reviewed-by: Jason Lowe-Power Reviewed-by: Pau Cabre Reviewed-by: Andreas Sandberg Maintainer: Andreas Sandberg --- src/arch/arm/linux/system.cc | 68 ++++++++++++++++++++++++++++++------ src/arch/arm/linux/system.hh | 14 ++++++++ src/arch/generic/linux/threadinfo.hh | 54 ++++++++++++++++++++++------ 3 files changed, 114 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/arch/arm/linux/system.cc b/src/arch/arm/linux/system.cc index 52e075d8f..7f0647512 100644 --- a/src/arch/arm/linux/system.cc +++ b/src/arch/arm/linux/system.cc @@ -236,7 +236,14 @@ void LinuxArmSystem::startup() { if (enableContextSwitchStatsDump) { - dumpStatsPCEvent = addKernelFuncEvent("__switch_to"); + if (!highestELIs64()) { + dumpStatsPCEvent = + addKernelFuncEvent("__switch_to"); + } else { + dumpStatsPCEvent = + addKernelFuncEvent("__switch_to"); + } + if (!dumpStatsPCEvent) panic("dumpStatsPCEvent not created!"); @@ -276,25 +283,64 @@ LinuxArmSystem::dumpDmesg() 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; diff --git a/src/arch/arm/linux/system.hh b/src/arch/arm/linux/system.hh index 709776ffc..247ebae13 100644 --- a/src/arch/arm/linux/system.hh +++ b/src/arch/arm/linux/system.hh @@ -132,6 +132,20 @@ class DumpStatsPCEvent : public PCEvent {} 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; }; diff --git a/src/arch/generic/linux/threadinfo.hh b/src/arch/generic/linux/threadinfo.hh index 6a5d031fa..89db36a30 100644 --- a/src/arch/generic/linux/threadinfo.hh +++ b/src/arch/generic/linux/threadinfo.hh @@ -95,6 +95,9 @@ class ThreadInfo 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; @@ -109,33 +112,44 @@ class ThreadInfo } 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)) @@ -144,13 +158,19 @@ class ThreadInfo 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; @@ -162,23 +182,35 @@ class ThreadInfo 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 -- cgit v1.2.3