arm,sim: fix context switch stats dumps for ARM64/Linux
authorPaul Rosenfeld <prosenfeld@micron.com>
Tue, 4 Apr 2017 15:06:38 +0000 (09:06 -0600)
committerPaul Rosenfeld <prosenfeld@micron.com>
Thu, 22 Jun 2017 13:32:04 +0000 (13:32 +0000)
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>

src/arch/arm/linux/system.cc
src/arch/arm/linux/system.hh
src/arch/generic/linux/threadinfo.hh

index 52e075d8fcb8b3cb76ae4d2cd75b5c91e7bcc565..7f06475125c9505409e8f1600580060bbd17131f 100644 (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::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;
index 709776ffcecdc9aaedfefb89823f003ce99e763d..247ebae1355032d8bebc80b4270a0d29235e6622 100644 (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;
 };
 
 
index 6a5d031fa8d95af06a985f2f8d4cd58111d6428b..89db36a30375c5817527ff7f703e0f73061515f4 100644 (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