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 <curtis.dunham@arm.com> Reviewed-by: Jason Lowe-Power <jason@lowepower.com> Reviewed-by: Pau Cabre <pau.cabre@metempsy.com> Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com> Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
This commit is contained in:
@@ -236,7 +236,14 @@ void
|
||||
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!");
|
||||
|
||||
@@ -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::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);
|
||||
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)
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
|
||||
// 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);
|
||||
bool is_kernel = (mm == 0);
|
||||
if (is_kernel && (pid != 0)) {
|
||||
pid = -1;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user