14.5 スレッド認識の実装

スレッド認識は、おそらく実装で最も重要な部分です。

API での対応する呼び出しは、スレッドまたはタスクでの場合と同様に、getOSContextProvider() です。ここで、context実行コンテキストを意味します。API では、Java インタフェースのインスタンス IOSContextProvidergetOSContextProvider() によって返されることが予期されます。このインタフェースは、前述の IOSProvider と同じ JAR ファイル内のパッケージ com.arm.debug.extension.os.context にあります。

myos タスクが次のような C タイプだと仮定します。

typedef enum {
    UNINITIALIZED = 0,
    READY
} tstatus_t ;

typedef struct{
    uint32_t           id;
    char               *name;
    volatile tstatus_t status;
    uint32_t           stack[STACK_SIZE];
    uint32_t           *sp;
} task_t;

また、OS が常に現在実行中のタスクをタスク配列の最初の要素に保存すると仮定すると、さらに追加で行うコールバックが実装されて現在実行中の(またはスケジュール済みの)タスクおよびすべてのタスク(スケジュール済みのタスクとスケジュールされていないタスクの両方)を新しい contexts.py ファイルに返します。

<some folder>
    /mydb
        /OS
            /myos
                /extension.xml
                /messages.properties
                /provider.py
                /contexts.py
  • provider.py
    # this script implements the Java interface IOSProvider
    from osapi import DebugSessionException
    from contexts import ContextsProvider
    
    def areOSSymbolsLoaded(debugger):
        […]
    
    def isOSInitialised(debugger):
        […]
    
    def getOSContextProvider():
        # returns an instance of the Java interface IOSContextProvider
        return ContextsProvider()
    
    def getDataModel():
        […]
  • contexts.py
    from osapi import ExecutionContext
    from osapi import ExecutionContextsProvider
    
    # this class implements the Java interface IOSContextProvider
    class ContextsProvider(ExecutionContextsProvider):
        def getCurrentOSContext(self, debugger):
            task = debugger.evaluateExpression("tasks[0]")
            return self.createContext(debugger, task)
            
        def getAllOSContexts(self, debugger):
            tasks = debugger.evaluateExpression("tasks").getArrayElements()
            contexts = []
            for task in tasks:
                if task.getStructureMembers()["status"].readAsNumber() > 0:
                    contexts.append(self.createContext(debugger, task))
            return contexts
            
        def getOSContextSavedRegister(self, debugger, context, name):
            return None
    
        def createContext(self, debugger, task):
            members = task.getStructureMembers()
            id = members["id"].readAsNumber()
            name = members["name"].readAsNullTerminatedString()
            context = ExecutionContext(id, name, None)
            return context

getOSContextSavedRegister() はまだ実装されていませんが、OS 認識が有効になるとすぐにデバッガで[Debug Control]ビューに OS タスクを入力するには、これで十分です。

図 14-7 myos[Debug Control]ビューのデータ
myos[Debug Control]ビューのデータ


タスクのレジスタ値がコアのレジスタから直接読み取られるため、現在実行中のタスクのコールスタックのデコードと、そのタスクの特定のスタックフレームのローカル変数の検査は、追加の変更なしに動作します。ただし、スケジュールされていないタスクの場合、切り替えるコンテキストで OS によって保存されたレジスタ値を読み取るために、getOSContextSavedRegister() を実装する必要があります。これらの値を読み取る方法は、OS のコンテキスト切り替えロジックによってまったく異なります。

これが myos の実装で、タスクが OS スケジューラによって切り替えられた場合にレジスタがスタックにプッシュされる M クラス ARM プロセッサの通常のコンテキスト切り替えルーチンに基づいています。

from osapi import ExecutionContext
from osapi import ExecutionContextsProvider

STACK_POINTER = "stack pointer"
REGISTER_OFFSET_MAP = {"R4":0L,    "R5":4L,  "R6":8L,   "R7":12L,
                       "R8":16L,   "R9":20L, "R10":24L, "R11":28L,
                       "R0":32L,   "R1":36L, "R2":40L,  "R3":44L,
                       "R12":48L,  "LR":52L, "PC":56L,  "XPSR":60L,
                       "SP":64L}

# this class implements the Java interface IOSContextProvider
class ContextsProvider(ExecutionContextsProvider):

    def getCurrentOSContext(self, debugger):
        […]

    def getAllOSContexts(self, debugger):
        […]

    def getOSContextSavedRegister(self, debugger, context, name):
        offset = REGISTER_OFFSET_MAP.get(name)
        base = context.getAdditionalData()[STACK_POINTER]
        addr = base.addOffset(offset)

        if name == "SP":
            # SP itself isn’t pushed onto the stack:return its computed value
            return debugger.evaluateExpression("(long)" + str(addr))
        else:
            # for any other register, return the value at the computed address
            return debugger.evaluateExpression("(long*)" + str(addr))

    def createContext(self, debugger, task):
        members = task.getStructureMembers()
        id = members["id"].readAsNumber()
        name = members["name"].readAsNullTerminatedString()
        context = ExecutionContext(id, name, None)
        # record the stack address for this task in the context’s
        # additional data; this saves having to look it up later in
        # getOSContextSavedRegister()
        stackPointer = members["sp"].readAsAddress()
        context.getAdditionalData()[STACK_POINTER] = stackPointer
        
        return context

これで、デバッガは保存されたレジスタの値を取得できます。これにより、スケジュールされていないタスクのスタックを展開できます。

注:

[Commands]ビューに「info threads」と入力すると、[Debug Control]ビューと同様の情報が表示されます。
非機密扱いPDF file icon PDF 版ARM DUI0446ZJ
Copyright © 2010–2016 ARM.All rights reserved.