diff --git a/src/core/hle/service/gsp.cpp b/src/core/hle/service/gsp.cpp
index 15e9d19a57..aabcb48db8 100644
--- a/src/core/hle/service/gsp.cpp
+++ b/src/core/hle/service/gsp.cpp
@@ -126,6 +126,10 @@ void TriggerCmdReqQueue(Service::Interface* self) {
         GPU::Write<u32>(GPU::Registers::CommandListAddress, cmd_buff[1] >> 3);
         GPU::Write<u32>(GPU::Registers::CommandListSize, cmd_buff[2] >> 3);
         GPU::Write<u32>(GPU::Registers::ProcessCommandList, 1); // TODO: Not sure if we are supposed to always write this
+
+        // TODO: Move this to GPU
+        // TODO: Not sure what units the size is measured in
+        g_debugger.CommandListCalled(cmd_buff[1], (u32*)Memory::GetPointer(cmd_buff[1]), cmd_buff[2]);
         break;
 
     case GXCommandId::SET_MEMORY_FILL:
diff --git a/src/video_core/gpu_debugger.h b/src/video_core/gpu_debugger.h
index ace9de95f7..4dafd31466 100644
--- a/src/video_core/gpu_debugger.h
+++ b/src/video_core/gpu_debugger.h
@@ -11,11 +11,23 @@
 #include "common/log.h"
 
 #include "core/hle/service/gsp.h"
-
+#include "pica.h"
 
 class GraphicsDebugger
 {
 public:
+    // A few utility structs used to expose data
+    // A vector of commands represented by their raw byte sequence
+    struct PicaCommand : public std::vector<u32>
+    {
+        Pica::CommandHeader& GetHeader()
+        {
+            return *(Pica::CommandHeader*)&(front());
+        }
+    };
+
+    typedef std::vector<PicaCommand> PicaCommandList;
+
     // Base class for all objects which need to be notified about GPU events
     class DebuggerObserver
     {
@@ -40,6 +52,16 @@ public:
             ERROR_LOG(GSP, "Received command: id=%x", cmd.id);
         }
 
+        /**
+        * @param lst command list which triggered this call
+        * @param is_new true if the command list was called for the first time
+        * @todo figure out how to make sure called functions don't keep references around beyond their life time
+        */
+        virtual void CommandListCalled(const PicaCommandList& lst, bool is_new)
+        {
+            ERROR_LOG(GSP, "Command list called: %d", (int)is_new);
+        }
+
     protected:
         GraphicsDebugger* GetDebugger()
         {
@@ -66,12 +88,39 @@ public:
                         } );
     }
 
+    void CommandListCalled(u32 address, u32* command_list, u32 size_in_words)
+    {
+        // TODO: Decoding fun
+
+        // For now, just treating the whole command list as a single command
+        PicaCommandList cmdlist;
+        cmdlist.push_back(PicaCommand());
+        auto& cmd = cmdlist[0];
+        cmd.reserve(size_in_words);
+        std::copy(command_list, command_list+size_in_words, std::back_inserter(cmd));
+
+        auto obj = std::pair<u32,PicaCommandList>(address, cmdlist);
+        auto it = std::find(command_lists.begin(), command_lists.end(), obj);
+        bool is_new = (it == command_lists.end());
+        if (is_new)
+            command_lists.push_back(obj);
+
+        ForEachObserver([&](DebuggerObserver* observer) {
+                            observer->CommandListCalled(obj.second, is_new);
+                        } );
+    }
+
     const GSP_GPU::GXCommand& ReadGXCommandHistory(int index) const
     {
         // TODO: Is this thread-safe?
         return gx_command_history[index];
     }
 
+    const std::vector<std::pair<u32,PicaCommandList>>& GetCommandLists() const
+    {
+        return command_lists;
+    }
+
     void RegisterObserver(DebuggerObserver* observer)
     {
         // TODO: Check for duplicates
@@ -94,4 +143,7 @@ private:
     std::vector<DebuggerObserver*> observers;
 
     std::vector<GSP_GPU::GXCommand> gx_command_history;
+
+    // vector of pairs of command lists and their storage address
+    std::vector<std::pair<u32,PicaCommandList>> command_lists;
 };