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:
Paul Rosenfeld
2017-04-04 09:06:38 -06:00
parent 9cdfcf9347
commit 1d7ff84f12
3 changed files with 116 additions and 24 deletions

View File

@@ -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;

View File

@@ -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;
};

View File

@@ -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