【问题标题】:How to trace execution of an existing python twisted application如何跟踪现有 python 扭曲应用程序的执行
【发布时间】:2014-07-08 17:16:00
【问题描述】:

所以……你们中的许多人可能会从你的职业生涯中认识到这一点。

您继承了包含数百个文件、数千行的代码库,每个文件都混合了对象、闭包和全局变量,没有进行单元测试,而且其中很多实际上可能是不再被调用的死代码。一个人(他是一个超级聪明的人,但也是一个彻头彻尾的黑客)写了大部分(就像所有这些),但他已经不在了。有趣的 python 怪癖比比皆是,它们做了想要的事情,但没有 cmets 来表明它们为什么存在。

d = dict(d)

其中 d 已经是字典(传递到方法中),是我个人迄今为止最喜欢的...(出于某种原因,浅拷贝 - 我花了一些时间来解决这个问题)不同路径使用的通用方法系统并知道他们需要做什么

if <passed_in_param>["XXX"] = <something>:
    <pathA>
    return A
elif <passed_in_param>["ZZZ"] = <somethingElse>:
    <pathB>
    return B

这是……呃……等等等等等等 - 只是那些渴望被重构的常见东西。

我真正的问题是,我需要先了解应用程序的工作原理,然后才能真正开始调试它......再次 - 我敢肯定,这对许多人来说是熟悉的领域。

系统使用twisted来托管一堆http端点(使用twistd),并且有很多端点占据了不同的根。每个都做“有趣”的事情,但基本上它们都与各种数据库或第三方休息端点通信,然后聚合一些东西格式化一些东西并返回一个休息响应。 Twisted 是一个非常好的框架!

现在,有什么方法可以让扭曲的框架帮助我开始调试它?像设置一些标志这样简单的事情,以便每当触发回调时 - 它会打印传递给回调的方法和参数?这不是我能够在文档中找到的内容。

你们是做什么的?欢迎提供任何提示和答案。

如果我也有的话,我不会在我的本地开发盒上扭曲猴子补丁。

更新 - 一些骇人听闻的进展............ 我自己并没有超越一些 hackyness,我想出了一些比没有好一点的东西。

在 tap.py 文件中,twistd 启动器启动我已经把这个(以防其他人觉得这很有用):

trace_exclusions = ["twisted", "DQL2SQL", "DDBClient", "exon_parser", "ontology", "pglib"]

def tracefunc(frame, event, arg, indent=[0]):
    if frame.f_code.co_filename.startswith("<!redacted!>/src/"):
        if not any(te in frame.f_code.co_filename for te in trace_exclusions):
            if event == "call":
                indent[0] += 2
                print "-" * indent[0], frame.f_code.co_filename.replace("/<!redacted!>/src/", ""), frame.f_code.co_name, frame.f_code.co_varnames
            if event == "return":
                indent[0] -= 2
    return tracefunc

import sys
sys.settrace(tracefunc)

我得到了一些有用的输出:

-- servers/http/base.py getResourceFor ('self', 'request', 'path', 'full_path', 'channel', 'category', 'service', 'version', 'market', 'locale', 'currency', 'query_id', 'extra_id')
-- servers/carhire_api/carhire_api.py render_GET ('self', 'request', 'entity_id', 'distance', 'dql', 'd', 'response')
---- servers/carhire_api/carhire_api.py get_int_parameter ('request', 'param_key')
---- servers/carhire_api/carhire_api.py get_int_parameter ('request', 'param_key')
---- beautify/deferred_entity_beautify.py get_entity_id ('pkid', 'val', 'iface', 'd')
------ cache/apc_cache.py __getitem__ ('self', 'key')
------ beautify/deferred_entity_beautify.py get_interface_id ()
------ kraken/client.py get ('self', 'key', 'd')
-------- kraken/client.py _get_multi ('self', 'connection', 'keys', 'task', 'd')
---------- kraken/twist.py get ('self', 'namespace', 'keys', 'task', 'the_size', 'req', 'r', 'the_timeout', 'l', 'idx', 'tmp_keys', 'd_l', 'exc')
------------ kraken/__init__.py get ('self', 'namespace', 'keys', 'req')
-------------- kraken/__init__.py request ('self', 'method', 'namespace', 'data', 'req')
---------------- kraken/__init__.py <genexpr> ('.0', 'x')
---------------- kraken/__init__.py <genexpr> ('.0', 'x')

但如果有人有更好的 - 我希望知道!

【问题讨论】:

  • 哦 - 我忘了提。我已经尝试使用带有打印方法名称的东西的 sys.settrace() 函数。显然,扭曲的反应堆会发送 lot 或消息。我可能会设置一个排除集或其他东西,但如果 twisted 有一个不错的调试选项,我很想知道它
  • 您是否考虑在 Twisted 的代码库或您继承的代码库中的战略位置设置 PDB 断点?然后,您可以单步执行代码、检查堆栈、局部变量等。
  • 是的 - 我让它在调试器中运行并使用断点。问题是因为我真的不知道断点在哪里,所以我有点卡住了。一旦您离开代码并最终进入反应器循环(在您注册了一些延迟之后),那么您可以在再次退出之前按下 [F6] 可能 forever
  • 我明白了。但是,如果不知道您究竟想了解有关该应用程序的什么,很难给出具体的建议。但我发现检查调用堆栈(PDB 中的w)和上下移动堆栈帧(ud)在这种情况下通常会有所帮助。
  • 是的,真正的问题是,通过延迟,您正在注册未来,然后,在某个时刻(信息已进入套接字)您的代码将触发。当那个东西触发时,你不再有它在代码中被声明为未来的上下文......所以我可以断点一个看起来很有趣的方法,我什至可以在调用堆栈中暂停,但我无法找到这段逻辑在哪里注册发生。调用堆栈最终进入扭曲并结束......

标签: python debugging refactoring twisted


【解决方案1】:

将其添加为原始帖子已经足够长了。除了 Jean-Paul 所指出的问题之外,我还在 tap.py 中编写了一个启动应用程序的 hack。我会把它贴在这里,以防将来对任何人有用。它确实为我提供了我需要的信息——当我到达一个端点时会发生这种情况......显然,将它与原始延迟链联系起来的东西会更棒。我从 Jean-Paul 发布的链接中获取了其中的一些代码 - 谢谢。

trace_exclusions = [
    "twisted",
    "DQL2SQL",
    "DDBClient",
    "exon_parser",
    "ontology",
    "pglib",
    "servers/interfaces/counter.py",
    "event_feed/feed_register.py",
    "database_notification_manager",
    "revenue_optimization"
    ]

def extractArgs(frame):
    co = frame.f_code
    dict = frame.f_locals
    n = co.co_argcount
    if co.co_flags & 4: n = n+1
    if co.co_flags & 8: n = n+1
    result = {}
    for i in range(n):
        name = co.co_varnames[i]
        if name != "self":
            r = dict.get(name, "----")
            r = str(r)
            if len(r) > 30:
                r = r[0:28] + '..'
            result[name] = r 
    return result

def tracefunc(frame, event, arg, indent=[0]):
    if frame.f_code.co_filename.startswith("/home/developer/projects/befogg/servers/nlp/src/"):
        if not any(te in frame.f_code.co_filename for te in trace_exclusions):
            if event == "call":
                indent[0] += 1
                print "-" * indent[0], frame.f_code.co_filename.replace("/home/developer/projects/befogg/servers/nlp/src/", ""), frame.f_code.co_name, "VARS:", frame.f_code.co_varnames
                print "-" * indent[0], frame.f_code.co_filename.replace("/home/developer/projects/befogg/servers/nlp/src/", ""), frame.f_code.co_name, "ARGS:", extractArgs(frame)
            if event == "return":
                indent[0] -= 1
    return tracefunc

import sys
sys.settrace(tracefunc)

【讨论】:

    【解决方案2】:

    一些工具已经开始使用Deferred,特别是更容易调试。有一些关于https://twistedmatrix.com/trac/ticket/1402 的信息以及引用自https://twistedmatrix.com/trac/ticket/4304 的分支中的一些代码。这些工具实际上还不是 Twisted 的一部分,所以尝试使用它们有点不方便。在最坏的情况下,也许他们会给你一些想法,也许你实际上会找到一种在你的应用程序中使用它们的方法。

    类似于您已经编写的基于跟踪挂钩的跟踪器,有twisted.python.util.spewerepsilon.spewer.Spewer。不过,这些实际上可能不会提供比您已经构建的工具更多的信息。

    【讨论】:

    • 谢谢 - 永远感谢您的帮助。您过去没有使用过任何狡猾的技巧吗?
    • 大部分时间都是花很长时间盯着代码,然后为它编写单元测试。 ://
    • 我将此标记为已接受。摆脱这个漏洞的唯一真正方法是开始研究系统应该做什么并为其编写一套全面的测试......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多