しおメモ

雑多な技術系ブログです。ニッチな内容が多いです。

LLDB経由でPythonを利用してStringの示すURLを開く

iOS開発をしていてデバッグする際に、コピーしたり、"を外したりするのが地味にめんどくさいので作ってみました。

今回は、SwiftのStringopenurl hogeのようなコマンドで開くのを目指します。(Swift以外でもほぼ同じです)

openurlコマンドの作成

コマンドの追加

コマンドの雛形はこちらです。(過去記事: LLDBからRxSwiftのdebugを仕込む - しおメモ)

このopenの中身を実装していきます。

#!/usr/bin/env python3

import lldb

def open(debugger, exp, result, dict):
    # ...

def __lldb_init_module(debugger, internal_dict):
    debugger.HandleCommand('command script add -f openurl.open openurl')

String値をPythonの変数に格納する

まずLLDBのstack frameを取得して、そこで該当の変数を評価してPython側の変数に格納します。

lldb.llvm.org

def evaluate(exp):
    value = (
        lldb.debugger.GetSelectedTarget()
        .GetProcess()
        .GetSelectedThread()
        .GetSelectedFrame()
        .EvaluateExpression(exp)
    )

    return value

ドキュメントを読む限りでは、lldb.frameで一発で取得できそうなのですが、実際にはinteractiveに動かした場合しか取得できなかったので、一つずつたどっています。

SBValueの変換

ここで取得したvalueは、SBValueというtypeなので、扱いやすい文字列に直していきます。 ドキュメントで不足している情報は、script helpで取得できます。
(とくにLLDBのPythonスクリプトに関してはあまり情報がないので、script helpやLLDB自体のドキュメントを見たほうが良いです)

(lldb) script help(lldb.SBValue)

こちらを見ると、GetValue()というchar const *を返すメソッドがあるのですが、こちらを使うと0x0000600000a1e610のような単なるアドレスの文字列が返ってきてしまうので、GetObjectDescription()を利用します。

value = evaluate(exp)
url = value.GetObjectDescription().replace('"', '')

SwiftのStringの場合は"がついてしまうので外しておきます。

同様に、型名を取得することもできます。

typename = value.GetTypeName()

こちらはSwift.StringFoundation.NSMutableStringといった文字列が入ります。

Pythonを利用してURLを開く

あとは、urllibosなどのPythonの機能を利用します。 ブラウザを開くのに、macOSのopenコマンドを利用しています。

実装例は以下のとおりです。

def open(debugger, exp, result, dict):
    value = evaluate(exp)
    typename = value.GetTypeName()
    if typename not in ['Swift.String', 'Foundation.NSMutableString']:
        print('Incompatible type: ' + typename)
        return

    url = value.GetObjectDescription().replace('"', '')
    u = urlparse(url)
    if u.scheme in ['http', 'https']:
        os.system('open ' + url)
    else:
        print('Failed to open: ' + url)

subprocess.runなどではこの場合動かないので、os.systemを利用しています。

LLDB上で実行する

実際にスクリプトをロードして実行してみます。

let urlString: String = "https://www.google.com/"
(lldb) command script import ~/dotfiles/lldb/openurl.py
(lldb) openurl urlString

実行すると、ブラウザで該当のURLが開かれます。

余談

StringからもQuick Lookで画像確認したいというモチベーションで、最初はdebugQuickLookObject()というメソッドを利用しようと思ったのですが、@objcが必須っぽかったので、こちらは断念しました。