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]ビューのデータ
タスクのレジスタ値がコアのレジスタから直接読み取られるため、現在実行中のタスクのコールスタックのデコードと、そのタスクの特定のスタックフレームのローカル変数の検査は、追加の変更なしに動作します。ただし、スケジュールされていないタスクの場合、切り替えるコンテキストで 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]ビューと同様の情報が表示されます。