pc能不能调试ios_pc flutter调试 ios - CSDN
  • 前端PC/APP项目调试方式来源: http://uedfamily.com/2017/01/19/pqj/%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E4%BE%BF%E6%8D%B7%E4%B9%8B%E8%B7%AF-%E8%B0%83%E8%AF%95%E7%AF%87/#morepc项目的调试方法pc上的调试...

    PC/APP前端项目调试方式

    来源:
    http://uedfamily.com/2017/01/19/pqj/%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E4%BE%BF%E6%8D%B7%E4%B9%8B%E8%B7%AF-%E8%B0%83%E8%AF%95%E7%AF%87/#more

    pc项目的调试方法

    pc上的调试的好像没什么东西,首选你要选择一个用的习惯的浏览器,然后找到报错或者问题所在的地方打个断点跟下去就行了。。。

    浏览器选择

    基本来说比较受欢迎的应该就是chrome和firefox了,比较推荐chrome,没有理由的推荐,不过firefox也还好。没什么大的差别。

    react项目调试

    浏览器中打断点

    当然首先你还得找到文件在哪,如果直接去看对应的index.jsx文件,你会看到怀疑人生的。

    正确的路径是这样的:
    打开控制台->点击sources标签栏->一堆列表中的webpack->点开之后,那个.文件夹->打开之后就是对应你src的目录了。
    因为es6等语法浏览器支持性不太好,所以我们浏览器下看到的和你写的代码好像有哪里不太对。

    本来是这样的:

    getLink(module){
        const key = module.get('key')
        return key ?<a href={module.get('val')} className="send-link" target="_blank">传送门:至 <b>{key}</b> 文档处</a>:null
    }
    

    看到的却是这样的:

    Header.prototype.getLink = function getLink(module) {
         var key = module.get('key');
         return key ? _react2['default'].createElement('a', { href: module.get('val'), className: 'send-link', target: '_blank' }, '传送门:至 ', _react2['default'].createElement('b', null, key), ' 文档处') : null;
    };
    

    其实也没什么差别,就是把es6的语法转化之后的语法。基本上找到该打断点的地方,跟进去调试就可以了。(这里就不介绍如何打断点了,聪明如你必须会,下面配一张调试时的截图)

    APP项目的调试方法

    app项目调试的话一般分一下几种情况。单纯的h5页面调试、模拟器调试、真机调试这三大类。下面简单介绍一下。

    纯h5页面调试

    首先,这里的纯h5页面是指不涉及到jsBridge的内容。和pc页面基本没有差别只不过运行在手机中。这样的调试的话可以直接在chrome中调试,只需要打开移动端调试模式即可。
    打开控制台,左上角有个类似手机的按钮。然后就可以看到页面已经变成适配不同屏幕了。简单粗暴,没什么好说的。

    模拟器调试

    如果用到native的部分功能的话,在浏览器上部分功能就没办法代替了。这样就需要模拟器了。因为大家基本用的都是mac。所以这里就介绍一下ios开发的工具xcode。

    1、下载xcode

    下载的话比较简单,直接在appstore搜索xcode就行。请在网络比较好的情况下下载,毕竟太大

    2、拉native代码

    需要找到native同学,拉下来对应app的代码。毕竟xcode只是个工具,具体app什么样还是需要native代码的。公司内部的话比较麻烦需要申请各种权限,慢慢来就行。

    3、启动模拟器

    等正确代码拉下来之后,在项目里找到 app名.xcworkspace文件,用xcode打开,应该会报错,请在ios同学+搜索引擎的帮助下解决。
    正常启动成功之后,就会打开一个模拟器。

    4、调试h5页面

    完全启动之后,进入用到要调试的页面,你就需要打开safari浏览器了。毕竟苹果的配套全家桶。

    5、进入要调试的webivew页面

    在safari浏览器中【开发】->【iOS Simulator】->【正在调试的网站】打开对应的调试窗口
    在弹出的调试窗口中,可以看到当前正在加载网页的各种信息,包括源码、请求头、图片、加载的资源与脚本、控制台输出等。

    (在执行这一步的时候,有人可能在safari中没有找到[开发]按钮,则需要在safari的偏好设置–》高级–》勾选开发者模式(在最下面,具体见图))

    还有一个点需要注意的是:开发】->【iOS Simulator】->【正在调试的网站】,中【正在调试的网站】仅出现你正在调试的几个页面地址。刚开始我选择ios simulator时发现:没有可以选择的网址。

    更多可以参考这篇博文:http://blog.fantasy.codes/front-end/2014/01/11/debug-ios-with-safari/

    并且它和chrome的调试方式相同,你可以直接修改网页的CSS样式,对网页布局等进行修改,而不用重新运行整个App。

    接下来就是愉快的调试吧

    android真机调试

    真机调试的话,ios比较麻烦需要的东西比较多。安卓调试就比较方便了,下面介绍一下如何调试安卓。

    1、启动手机上的开发者模式

    这个分手机而异,应该要在关于设备中查找。实在不行,外事不决问百度。
    USB链接电脑和手机

    2、Chrome中调试

    打开电脑中的 Chrome,浏览器中输入 chrome://inspect ,进入后勾选 Discover USB devices,就可以看到手机的 Chrome 上打开的页面了。

    点击 inspect,会在新窗口打开一个 Chrome Developer Tools 的页面,在此页面输入网址便可以看到手机上加载了相同的网址,顺利的话就手机上就打开页面了,即可以调试了。

    到这里关于调试的介绍就完了,都是些基本介绍。抛个砖而已,深入了解可以自行专研一下。

    ios真机调试

    对于比较麻烦的ios真机测试还是简单介绍一下,ios调试还是离不开xcode的。这是ios开发必备工具。

    1、登录开发者中心,创建APP ID;

    将你的设备(iPhone或iPad)注册到开发者中心,需要安装一个APP获取设备的UUID,APP Store搜索可以得到很多这样的APP;

    2、创建一个证书,证书分为开发证书和发布证书两种。调试阶段使用开发证书即可;

    3、创建一个Provisioning Profile 文件,同样也分开发和发布两种,这里创建开发版即可。创建时,需要选择一个开发者证书,选择允许调试的设备。

    好了,将设备用USB连接到电脑,在Xcode中选择你的设备,运行APP,就可以进行真机调试了。

    小结

    PC上调试、模拟器调试以及android真机上调试,亲测按照上述步骤都是ok的。确实又涨知识了。

    展开全文
  • IOS11登录时遇到一个请求与PC返回不一致情况,在小程序调试IOS上始终没有wx.request()不能发送请求 尝试解决方法  打开微信小程序调试的设置,将TLS设为可信任的域名 设置 --->项目设置 转载于:...

      IOS11登录时遇到一个请求与PC返回不一致情况, 在小程序调试时IOS上始终没有wx.request() 不能发送请求

    尝试解决方法

      打开微信小程序调试的设置, 将TLS设为可信任的域名

    设置 ---> 项目设置

     

     

    转载于:https://www.cnblogs.com/zhongke/p/9699297.html

    展开全文
  • 简介: 在进行iOS开发中,APP尤其是游戏,在沙盒中可以存放游戏资源包或者数据,例如Json文件、...是很麻烦的,所以可以考虑在PC上替换APP内的沙盒目录中的AssetBundle包,便于调试iOS设备关于沙箱的操作像Andro

    简介:

    在进行iOS开发中,APP尤其是游戏,在沙盒中可以存放游戏资源包或者数据,例如Json文件、AssetBundle包,在使用服务器下载资源包,存放到本地沙盒中,可以大大减少APP本身的大小,当然,在调试的时候,如果频繁的更换服务器的AssetBundle包,是很麻烦的,所以可以考虑在PC上替换APP内的沙盒目录中的AssetBundle包,便于调试,iOS设备关于沙箱的操作不像Android设备的简单粗暴,但是也不麻烦,在Xcode中可以很快实现。

    步骤:

    环境:Xcode9,iOS9.3.5

    1.使用Xcode打开沙盒。

    在Xcode目录下,Window->Devices and Simulators,打开设备和模拟器列表

    2.打开目标APP的沙盒。

    选择需要操作的设备和APP,点击下方齿轮状设置按钮,选择Download Container,将该APP的沙盒文件下载至本地。

    3.保存当前APP的沙盒文件。


    4.打开沙盒内容。

    显示包内容。


    5.修改包内容。

    此时可以根据自己的需要,修改沙盒内容,例如Json,AssetBundle,xml


    6.将本地沙盒替换目标沙盒。

    选择需要替换的APP沙盒,点击下方齿轮状设置按钮,选择Replace Container替换沙盒。

    7.选择修改过的沙盒文件。

    结束语:

    通过以上步骤即可实现在PC端操作iOS设备的沙箱,简单来说,就是一出一入,使用该方式便于iOS开发中的调试,也希望对大家有帮助。

    展开全文
  • 微信公众号开发避免不了要用到微信的手机客户端程序。说实话,这个工具确实很好用。其中的移动调试确实能够调试发现不小问题。 特别是iPhone手机,问题最最多的。很多JS脚本在PC或者Android手机...另外iOS不支持win...

    微信公众号开发避免不了要用到微信的手机客户端程序。说实话,这个工具确实很好用。其中的移动调试确实能够调试发现不小问题。

    特别是iPhone手机,问题最最多的。很多JS脚本在PC或者Android手机是正常的。放到iOS就会莫名的报错。

    今天就发现了一个小问题。就是关于时间函数Date。 iOS浏览器不支持YYYY-MM-DD 的转换。它只支持YYYY/MM/DD格式。

    另外iOS不支持window.open 这种方式,支持window.location.href

    还有一个js replace函数。单独用只是替换第一个匹配的字符。

     

    转载于:https://www.cnblogs.com/liguoyi/p/5995179.html

    展开全文
  • iOS包含许多“秘密”调试工具,包括环境变量、偏好、GCB的常规调用,等等。本技术说明描述了这些工具。如果你开发iOS,你应该通过这个列表查看你错过的可以使你更轻松的工具。 简介 所有苹果系统包括苹果工程团队...

    iOS包含许多“秘密”调试工具,包括环境变量、偏好、GCB的常规调用,等等。本技术说明描述了这些工具。如果你开发iOS,你应该通过这个列表查看你错过的可以使你更轻松的工具。

    简介

    所有苹果系统包括苹果工程团队添加的调试工具都是用来帮助开发和调试特定子系统。这些工具属于已发布系统软件,可以使用它们来调试代码。本技术手册描述一些广泛使用的工具。调试工具记录在另一个地方,有个简短的工具概述并链接到现有文档。

    重要:这不是一个详尽的清单:并没有记录所有的调试工具。

    本技术说明中的许多细节在不同的平台和版本不一样。因此,你可能遇到平台间的小差异以及新旧系统的差异。已知的重大变化在文章中列出。

    本技术说明是参照iOS4.1写的。

    警告:本技术说明中描述的调试工具是无根据的。苹果有权依据iOS的演变改变或消除每个工具;这在过去已经发生,并且在未来也有可能发生。这些工具只用于调试:不能发布一个依赖本技术说明中存在或描述工具功能的产品

    除了兼容二进制这个技术问题,记住,iOS应用程序必须遵守各种法律协议和应用商店审查指南。

    注意:如果你也开发Mac OS X,你可能希望阅读Technical Note TN2124, 'Mac OS X Debugging Magic'.

    本技术说明包括先进的调试技术。如果你是刚刚开始,你应该先阅读如下材料“

    GDB是系统的主要调试工具。查看GDB完整描述,可参阅GDB调试(Debugging with GDB)。

    Xcode是平跟的集成开发环境(IDE)。它包括一个复杂的图形调试器,作为GDB的包装。关于Xcode的更多信息,可参阅苹果开发者参考库中的“语言和工具”章节。

    本技术说明不包括性能调试。如果你想调试性能问下,最好从开始使用性能(Getting Started with Performance )文档开始

    基础知识

    本技术说明中后面的部分将详细描述调试工具。许多工具使用类似的技术来启用或禁用工具,并看到输出。本章节描述这些常见的技术。

    启用调试设备

    一些调试工具默认是启用的。然而,大多数工具必须使用下面章节描述的技术来启用。

    环境变量

    在许多情况下,你可以通过设置一个特定的环境变量来启用调试工具。你可以通过Xcode中的可执行检查器来实现,如图1所示。

    Xcode中设置环境变量

     

    偏好设置

    一些调试工具可以通过设置特殊的偏好来启用。你可以通过配置Xcode中命令行参数来设置调试偏好,如图2所示。

    Xcode中设置命令行参数

     

    可调用的程序

    许多系统框架包括打印调试信息到stderr的程序。专门设计这些程序用来调用GDB,或者他们可能是调试时非常有用的API程序。清单1展示了如何调用GDB调试程序的例子,特别的,它调用CFBundleGetAllBundles 获取应用中加载的所有bundles 的列表,通过调用CFShow 程序来打印列表。

    清单1 调用GDB调试程序

    (gdb) call (void)CFShow((void *)CFBundleGetAllBundles())
    <CFArray 0x10025c010 [0x7fff701faf20]>{type = mutable-small, count = 59, values = (
        0 : CFBundle 0x100234d00 </System/Library/Frameworks/CoreData.framework> …
        […]
        12 : CFBundle 0x100237790 </System/Library/Frameworks/Security.framework> …
        […]
        23 : CFBundle 0x100194eb0 </System/Library/Frameworks/CoreFoundation.framework> …
        […]
    )}


    注意:当调用像这样的程序,GDB必须知道程序的返回类型(因为一些返回类型可以影响参数传递的方式)。如果调用一个没有调试标识的程序,你必须告诉GDB添加一个返回类型。例如,清单1传递CFBundleGetAllBundles 返回类型为 (void *) 和CFShow 返回类型为 (void)

    类似的,如果你调用一个采用非标准参数的程序,你需要传递参数确保GDB能正确的传递。

    如果你看不到程序的输出,你可能需要查看控制台日志,如调试输出章节所述。

    重要:如果在代码中使用这种技术,它并不总是为静态程序公正。编译器程序间的优化坑内会引起一个静态程序偏离标准函数调用ABI。这种情况下,通过GDB调用不可靠。

    在实践中,这只会影响使用英特尔32位代码的iPhone模拟器。

    配置概要文件

    一些iOS子系统可以通过安装一个特殊的配置概要文件,启用调试工具。查看推送通知可查看这样的例子。通常以这种方式安装配置概要文件:

    把它放到一个web服务器上,然后使用设备上的Safari下载

    附加到email,寄到设备上的账户,然后运行mail并打开配置文件的附近

    了解更多关于配置概要文件,可阅读iPhone商业网址上的各种文档。

    查看调试输出

    生成调试输出的程序通常使用下列机制:

    NSLog

    打印到stderr

    系统日志

    NSLog是一个高级的API用于日志,在Objective-C代码中广泛使用。NSLog确切行为非常复杂,并且随着时间的推移在不断的改变,这已超出了本文的范围。然而,我们可以了解nslog打印到stderr或者记录到系统日志或者两者。因此,如果你了解这两种机制,可以通过NSLog查看任何记录。

    打印到stderr是最常用的输出机制。鉴于这个主题的重要性,在下一节将深度覆盖。

    查看系统日志最简单的方法是在XcodeOrganizer 窗口中的控制台tab。如果用户不行安装Xcode,可以使用iPhone配置utilityiPhone Configuration Utility)查看系统日志。

    控制台输出

    许多程序,甚至许多系统框架,打印调试信息到stderr。输出的目的地最终是程序控制:它可以将stderr重定向到任何选择的目的地。然而,在大多数情况下,程序一般不重定向stderr,所以输出到默认的目的地,该目的地继承自启动环境程序。通常是下列情况之一:

    如果你启动一个GUI应用程序,同时这个应用程序也可能被普通用户启动,系统将任何打印到stderr的信息重定向到系统日志。你可以使用前面描述的技术来查看这些信息。

    如果你在Xcode中运行一个程序,你可以在Xcode的调试器控制台窗口(在运行菜单中选择控制台菜单项便可以看到这个菜单)看到stderr输出.

    附加到运行程序(使用Xcode中的附加到程序菜单或者GDB中附加命令)不会自动将程序的stderr连接到你的GDB窗口。你可以使用Technical Note TN2030, 'GDB for MacsBug Veterans'中“附加后查看stdoutstderr”章节中描述的技巧来完成。

    一些汇编要求

    现在不太可能需要编写大量的汇编语言代码,但对这有个基本的了解很有用。尤其是当你在调试,特别是当你调试库或框架崩溃,并且没有源代码。本节讨论一些基本的技巧有利于汇编级调试程序。具体的说,它描述了如何在所有支持的架构中设置断点,访问参数以及访问返回地址。

    重要的区别是在本技术说明中汇编级别例子来自运行在iPhone 3GS的armv7。然而,在大多数情况下差异并不显著,例子可能来自不同的架构,甚至来自Mac。很容易改编这样的例子到其他架构。最显著的差异是:

    访问参数

    获取返回地址

    这些将在以下章节讨论。

    重要提示:以下特定架构章节包含经验法则。如果程序有任何非标准参数或非标准函数结果,这些法则并不适用,你应阅读文档了解相关细节。

    在这种情况下,标准参数是interger(一个寄存器),枚举和指针(包括数组指针和函数指针)。非标准参数是浮点数,向量,结构,比一个寄存器大的interger以及在程序最后固定参数后的任何参数,该程序参数数量可变。

    了解所有iOS设备的调用约定详情,可查看iOS ABI函数调用指南(iOS ABI Function Call Guide.)。使用英特尔32位架构的iPhone模拟器在Max OS X ABI函数调用指南(Mac OS X ABI Function Call Guide.)。

    在你阅读以下章节前,了解GDB的精妙处是非常重要的。因为GDB本质上是一个源代码级别的调试器,当你在程序设置一个断点,GDB不会在程序的第一个指令处设置断点;相反,它在程序prologue后第一个指令处设置断点。从源代码级别调试来看,这样非常完美。在源代码级别调试器中,你不会想要单步调试程序prologue。然而,当在汇编级别调试中,非常容易在prologue运行前访问参数。这是因为程序第一条指令的参数的位置是由调用ABI函数决定的,但运行prologue自行决定搅乱周围的事情。此外,每个prologue可以以稍微不同的方式在做这件事。所以唯一访问已执行prologue后参数的方法是反编译prologue,搞清楚一切。这是典型但不总是很简单,但它仍然是额外的工作。

    告诉GDB在程序的第一个指令处设置断点最好的办法是在程序名字上价格前缀“*”。清单2展示了这样的例子。

    清单2 prologue前后

    (gdb) b CFStringCreateWithFormat
    Breakpoint 1 at 0x34427ff8
    (gdb) info break
    Num Type           Disp Enb Address    What
    1   breakpoint     keep n   0x34427ff8 <CFStringCreateWithFormat+8>
    -- The breakpoint is not at the first instruction.
    -- Disassembling the routine shows that GDB has skipped the prologue.
    (gdb) x/5i CFStringCreateWithFormat
    0x34427ff0 <CFStringCreateWithFormat>: push    {r2, r3}
    0x34427ff2 <CFStringCreateWithFormat+2>: push    {r7, lr}
    0x34427ff4 <CFStringCreateWithFormat+4>: add r7, sp, #0
    0x34427ff6 <CFStringCreateWithFormat+6>: sub sp, #4
    0x34427ff8 <CFStringCreateWithFormat+8>: add r3, sp, #12
    -- So we use a "*" prefix to disable GDB's 'smarts'.
    (gdb) b *CFStringCreateWithFormat
    Breakpoint 2 at 0x34427ff0
    (gdb) info break
    Num Type           Disp Enb Address    What
    1   breakpoint     keep n   0x34427ff8 <CFStringCreateWithFormat+8>
    2   breakpoint     keep n   0x34427ff0 <CFStringCreateWithFormat>


    相比之下,在本文档中其他列表是在Mac OS X上创建的,使用传统GDB注释。重要:因为iOS无法从命令行运行程序,像清单2中的iOS特殊列表来自于Xcode控制台窗口。由于控制台窗口不支持“#”字符注释,所以注释用‘--’来表示。如果你想自己重复这些例子,你不能在控制台窗口输入--

    最后,如果你正在寻找关于具体说明信息,Shark(包括Xcode开发工具)中的帮助菜单中有一个ARM,英特尔和PowerPC 架构的指令集引用。

    ARM

    在ARM程序中,前面四个参数放在寄存器中。返回地址在寄存器LR中。表1展示了当你停在函数的第一个指令时,如何从GDB访问这些值。

    表1 ARM的访问参数

    What

    GDB Syntax

    return address

    $lr

    first parameter

    $r0

    second parameter

    $r1

    third parameter

    $r2

    fourth parameter

    $r3


    因为参数传递到寄存器中,在prologue之后没有简单的方法来访问参数。函数返回的结果在R0 ($r0)寄存器中。

    清单3展示了如何使用这些信息访问GDB参数。

    清单2 ARM参数

    -- We have to start the program from Xcode. Before we do that, we go to 
    -- Xcode's Breakpoints window and set a symbolic breakpoint on 
    -- CFStringCreateWithFormat.
    GNU gdb 6.3.50-20050815 […]
    -- We've stopped after the prologue.
    (gdb) p/a $pc
    $1 = 0x34427ff8 <CFStringCreateWithFormat+8>
    -- Let's see what the prologue has done to the registers 
    -- holding our parameters.
    (gdb) x/5i $pc-8
    0x34427ff0 <CFStringCreateWithFormat>: push    {r2, r3}
    0x34427ff2 <CFStringCreateWithFormat+2>: push    {r7, lr}
    0x34427ff4 <CFStringCreateWithFormat+4>: add r7, sp, #0
    0x34427ff6 <CFStringCreateWithFormat+6>: sub sp, #4
    0x34427ff8 <CFStringCreateWithFormat+8>: add r3, sp, #12
    -- Hey, the prologue hasn't modified R0..R3, so we're OK.
    --
    -- first parameter is "alloc"
    (gdb) p/a $r0
    $2 = 0x3e801d60 <__kCFAllocatorSystemDefault>
    -- second parameter is "formatOptions"
    (gdb) p/a $r1
    $3 = 0x0
    -- third parameter is "format"
    (gdb) call (void)CFShow($r2)
    managed/%@/%@
    -- It turns out the prologue has left LR intact as well.
    -- So we can get our return address.
    --
    -- IMPORTANT: Bit zero of the return address indicates that it lies 
    -- in a Thumb routine. So when using a return address in LR or on 
    -- the stack, always mask off bit zero.
    (gdb) p/a $lr & 0xfffffffe
    $4 = 0x34427e18 <__CFXPreferencesGetManagedSourceForBundleIDAndUser+48>
    -- Now clear the breakpoint and set a new one before the prologue.
    (gdb) del 1
    (gdb) b *CFStringCreateWithFormat
    Breakpoint 2 at 0x34427ff0
    (gdb) c
    Continuing.
    
    Breakpoint 2, 0x34427ff0 in CFStringCreateWithFormat ()
    -- We're at the first instruction. The parameters are guaranteed 
    -- to be in the right registers.
    (gdb) p/a $pc
    $5 = 0x34427ff0 <CFStringCreateWithFormat>
    -- first parameter is "alloc"
    (gdb) p/a $r0
    $6 = 0x3e801d60 <__kCFAllocatorSystemDefault>
    -- second parameter is "formatOptions"
    (gdb) p/a $r1
    $7 = 0x0
    -- third parameter is "format"
    (gdb) call (void)CFShow($r2)
    %@/%@.plist
    -- return address is in LR; again, we mask off bit zero
    (gdb) p/a $lr & 0xfffffffe
    $8 = 0x34427e7c <__CFXPreferencesGetManagedSourceForBundleIDAndUser+148>
    (gdb) b *0x34427e7c
    Breakpoint 3 at 0x34427e7c
    -- Delete the other breakpoint to avoid thread-based confusion.
    (gdb) del 2
    -- Continue to the return address.
    (gdb) c
    Continuing.
    
    Breakpoint 3, 0x34427e7c in __CFXPreferencesGetManagedSourceForBundleIDAndUser ()
    -- function result
    (gdb) p/a $r0
    $9 = 0x1062d0
    (gdb) call (void)CFShow($r0)
    mobile/.GlobalPreferences.plist


    英特尔32位

    iPhone模拟器使用的是英特尔32位架构。

    在英特尔32位程序中,参数传递到堆栈中。程序的第一个指令在栈顶,包含返回地址,下个字节包含第一个(最左边)参数,下个字节包含第二个参数,等等。表2展示了如何访问GDB的这些值。

    表2 英特尔32位的可访问参数

    What

    GDB Syntax

    return address

    *(int*)$esp

    first parameter

    *(int*)($esp+4)

    second parameter

    *(int*)($esp+8)

    ... and so on

     

    在程序prologue之后,可以访问帧指针参数(寄存器EBP)。表3展示了语法。

    表3 prologue之后可访问参数

    What

    GDB Syntax

    previous frame

    *(int*)$ebp

    return address

    *(int*)($ebp+4)

    first parameter

    *(int*)($ebp+8)

    second parameter

    *(int*)($ebp+12)

    ... and so on

     

    函数返回的结果在寄存器EAX ($eax)。

    清单4展示了如何使用信息访问GDB参数。

    清单4 英特尔32位参数

    $ # Use the -arch i386 argument to GDB to get it to run the 
    $ # 32-bit Intel binary.
    $ gdb -arch i386 /Applications/TextEdit.app
    GNU gdb 6.3.50-20050815 (Apple version gdb-1346) […]
    (gdb) fb CFStringCreateWithFormat
    Breakpoint 1 at 0x31ec6d6
    (gdb) r
    Starting program: /Applications/TextEdit.app/Contents/MacOS/TextEdit 
    Reading symbols for shared libraries […]
    Breakpoint 1, 0x940e36d6 in CFStringCreateWithFormat ()
    (gdb) # We've stopped after the prologue.
    (gdb) p/a $pc
    $1 = 0x940e36d6 <CFStringCreateWithFormat+6>
    (gdb) # However, for 32-bit Intel we don't need to inspect 
    (gdb) # the prologue because the parameters are on the stack.
    (gdb) # We can access them relative to EBP.
    (gdb) #
    (gdb) # first parameter is "alloc"
    (gdb) p/a *(int*)($ebp+8)
    $2 = 0xa0473ee0 <__kCFAllocatorSystemDefault>
    (gdb) # second parameter is "formatOptions"
    (gdb) p/a *(int*)($ebp+12)
    $3 = 0x0
    (gdb) # third parameter is "format"
    (gdb) call (void)CFShow(*(int*)($ebp+16))
    %@
    (gdb) # return address is at EBP+4
    (gdb) p/a *(int*)($ebp+4)
    $4 = 0x940f59fb <__CFXPreferencesGetNamedVolatileSourceForBundleID+59>
    (gdb) # Now clear the breakpoint and set a new one before the prologue.
    (gdb) del 1
    (gdb) b *CFStringCreateWithFormat
    Breakpoint 2 at 0x940e36d0
    (gdb) c
    Continuing.
    
    Breakpoint 2, 0x940e36d0 in CFStringCreateWithFormat ()
    (gdb) # We're at the first instruction. We must access 
    (gdb) # the parameters relative to ESP.
    (gdb) p/a $pc
    $6 = 0x940e36d0 <CFStringCreateWithFormat>
    (gdb) # first parameter is "alloc"
    (gdb) p/a *(int*)($esp+4)
    $7 = 0xa0473ee0 <__kCFAllocatorSystemDefault>
    (gdb) # second parameter is "formatOptions"
    (gdb) p/a *(int*)($esp+8)
    $8 = 0x0
    (gdb) # third parameter is "format"
    (gdb) call (void)CFShow(*(int*)($esp+12))
    managed/%@/%@
    (gdb) # return address is on the top of the stack
    (gdb) p/a *(int*)$esp
    $9 = 0x940f52cc <__CFXPreferencesGetManagedSourceForBundleIDAndUser+76>
    (gdb) # Set a breakpoint on the return address.
    (gdb) b *0x940f52cc
    Breakpoint 3 at 0x940f52cc
    (gdb) c
    Continuing.
    
    Breakpoint 3, 0x940f52cc in __CFXPreferencesGetManagedSourceForBundleIDAndUser ()
    (gdb) # function result
    (gdb) p/a $eax
    $10 = 0x1079d0
    (gdb) call (void)CFShow($eax)
    managed/com.apple.TextEdit/kCFPreferencesCurrentUser


    架构陷阱

    下面章节描述在汇编级调试时坑内个遇到的几个陷阱

    额外参数

    在查看汇编级参数时,要记住以下几点:

    如果程序是C++成员函数,第一个隐式参数是this

    如果程序是Objective-C方法,有两个隐式参数(有关详细信息,请参阅Objective-C)。

    如果编译器可以找到函数(通常是声明为static的函数)的所有调用者,它可以以非标准方式选择传递参数给函数。很少有高效基于寄存器ABI的架构,但是对于英特尔32位程序是非常常见的。因此,如果你在一个英特尔32位程序的静态函数上设置断点,小心这令人费解的行为。

    字节顺序和单位大小

    当检查GDB内存事,如果使用正确的单位大小,事情会更加顺利。表4GDB支持的单位大小的总结。

    表4 GDB单位大小

    Size

    C Type

    GDB Unit

    Mnemonic

    1 byte

    char

    b

    byte

    2 bytes

    short

    h

    half word

    4 bytes

    int

    w

    word

    8 bytes

    long or long long

    g

    giant

    当在低位优先系统(所有iOS设备和模拟器)上调试时这非常重要,如果你使用了错误的单位大小,你会得到一个令人困惑的结果。清单5展示了一个例子(来自Max,但在iOS设备上结果是相同的)。CFStringCreateWithCharacters 的第二个和第三个参数指定Unicode字符数组。每个元素都是一个UniChar,是本地优先格式的16位数字。当在低位优先系统运行时,必须使用正确的单位大小转储数组,否则看起来一团糟。

    清单5 使用正确的单位大小

    $ gdb
    GNU gdb 6.3.50-20050815 (Apple version gdb-1346) […]
    (gdb) attach Finder
    Attaching to process 4732.
    Reading symbols for shared libraries . done
    Reading symbols for shared libraries […]
    0x00007fff81963e3a in mach_msg_trap ()
    (gdb) b *CFStringCreateWithCharacters
    Breakpoint 1 at 0x7fff86070520
    (gdb) c
    Continuing.
    
    Breakpoint 1, 0x00007fff86070520 in CFStringCreateWithCharacters ()
    (gdb) # The third parameter is the number of UniChars 
    (gdb) # in the buffer pointed to by the first parameter.
    (gdb) p (int)$rdx
    $1 = 18
    (gdb) # Dump the buffer as shorts. Everything makes sense.
    (gdb) # This is the string "Auto-Save Recovery".
    (gdb) x/18xh $rsi
    0x10b7df292: 0x0041  0x0075  0x0074  0x006f  0x002d  0x0053  0x0061  0x0076
    0x10b7df2a2: 0x0065  0x0020  0x0052  0x0065  0x0063  0x006f  0x0076  0x0065
    0x10b7df2b2: 0x0072  0x0079
    (gdb) # Now dump the buffer as words. Most confusing.
    (gdb) # It looks like "uAotS-va eeRocevyr"!
    (gdb) x/9xw $rsi
    0x10b7df292: 0x00750041      0x006f0074      0x0053002d      0x00760061
    0x10b7df2a2: 0x00200065      0x00650052      0x006f0063      0x00650076
    0x10b7df2b2: 0x00790072
    (gdb) # Now dump the buffer as bytes. This is a little less 
    (gdb) # confusing, but you still have to remember that it's big 
    (gdb) # endian data.
    (gdb) x/36xb $rsi
    0x10b7df292: 0x41    0x00    0x75    0x00    0x74    0x00    0x6f    0x00
    0x10b7df29a: 0x2d    0x00    0x53    0x00    0x61    0x00    0x76    0x00
    0x10b7df2a2: 0x65    0x00    0x20    0x00    0x52    0x00    0x65    0x00
    0x10b7df2aa: 0x63    0x00    0x6f    0x00    0x76    0x00    0x65    0x00
    0x10b7df2b2: 0x72    0x00    0x79    0x00


    控制的崩溃

    在某些情况下,以可控的方式控制程序崩溃。一个常见的方法是终止调用。另一个选择是使用__builtin_trap内在函数,产生特定指令。清单6展示了如何实现。

    清单6 通过__builtin_trap崩溃

    int main(int argc, char **argv) {     __builtin_trap();     return 1; }


    如果你在这个调试器上运行程序,必须在调用__builtin_trap之前停止。否则程序将崩溃并生成崩溃报告。注意: __builtin_trap 内在函数在PowerPC 和ARM生成一个trap指令,在英特尔生成一个ud2a指令。

    警告:我们建议你在调试版本使用这个技术,发布版本应该使用abort

    需注意:iOS应用程序声明周期由用户控制,这意味着iOS应用程序不应该退出。只有当程序崩溃的情况下,你发布的版本需调用abort,调用abort可以防止破坏用户数据或者让你诊断问题更容易。

    工具

    工具是一个动态跟踪和分析代码的应用程序。它运行在Mac OS X 上,允许你的目标程序运行在Mac OS X,iOS设备,iPhone模拟器。

    而工具主要集中在性能调试,你也可以使用它来调试错误。例如,ObjectAlloc 工具可以帮助你追踪bug

    工具的一个特别好的功能是让你可以访问死机的电脑,有关详情和其他工具功能,请参阅工具用户指南( Instruments User Guide)。

    崩溃报告

    崩溃报告是一个非常重要的调试功能,可以记录程序崩溃的信息。它一直处于启用状态,你只需要查看它的输出。

    崩溃报告的详情在“理解和分析iPhone OS应用程序崩溃报告”(Technical Note TN2151, 'Understanding and Analyzing iPhone OS Application Crash Reports')。

    BSD

    BSD子系统实现过程、内存、文件和网络基础结构,从而对系统上所有应用程序至关重要。BSD实现一些方便的调试工具,这些工具你都可以使用。

    内存分配器

    默认的内存分配器中包含大量的调试工具,你可以通过环境变量启用。这些都记录在指南页面(manual page)。表5列出了一些更有用的环境变量。

    表5 一些有用的内存分配器环境变量

    变量

    概要

    MallocScribble

    先填充到分配的内存0xAA ,释放内存到0x55

    MallocGuardEdges

    在大内存分配之前或之后添加保护页面

    MallocStackLogging

    为每个内存block记录回溯以协助内存调试工具;如果block被分配并立即释放,两条记录将从日志中删除,这有助于减小日志大小。

    MallocStackLoggingNoCompact

    和MallocStackLogging 一样,但是保存所有日志记录

    如果默认的内存分配器检查到某些常见的编程问题,将记录日志。例如,如果你两次释放block的内存或者你释放未分配的内存,free将打印清单7中列出的消息。括号里的数字是进程id

    清单7 free打印的公共消息

    DummyPhone(1839) malloc: *** error for object 0x826600: double free *** set a breakpoint in malloc_error_break to debug


    最后,你可以使用malloc_zone_check(来自<malloc.h>)以编程的方式检查堆的一致性。你可以在GDB上运行你的程序并在malloc_error_break设置一个断点,来调试这类问题。一旦你点击断点,你可以使用GDB的 backtrace 命令决定调用者。

    标准C++库

    标准C++库支持大量的调试特性:

    设置_GLIBCXX_DEBUG编译时变量启用标准C++库中的调试模式。

    GCC4.2及更高版本不支持,在这种情况下不能使用。

    GCC4.0之前的版本,将环境变量GLIBCPP_FORCE_NEW设置为1并在标准C++库中禁用内存缓存。运行你使用其他内存调试工具调试C++内存分配。

    在GCC4.0 及之后版本这是默认的。

    动态链接(dyld)

    动态链接(dyld) 支持大量的调试工具,你可以通过环境变量启用。这些都记录在指南页面(manual page)。表6列出了一些更有用的环境变量。

    表6 动态链接环境变量

    变量

    概述

    DYLD_IMAGE_SUFFIX

    先用后缀搜索库

    DYLD_PRINT_LIBRARIES

    加载日志库

    DYLD_PRINT_LIBRARIES_POST_LAUNCH

    同上,但只在main运行后

    DYLD_PRINT_OPTS [1]

    打印启动命令参数

    DYLD_PRINT_ENV [1]

    打印启动环境变量

    DYLD_PRINT_APIS [1]

    记录dyld API调用(例如,dlopen

    DYLD_PRINT_BINDINGS [1]

    记录符号绑定

    DYLD_PRINT_INITIALIZERS [1]

    记录图像初始化打印

    DYLD_PRINT_SEGMENTS [1]

    记录分段映射

    DYLD_PRINT_STATISTICS [1]

    打印开始性能统计数据

    注意:只支持Mac OS X 10.4及之后版本。不过,支持所有版本的iOS

    虽然这些环境变量在iOS上实现,因为受限于系统环境,其中只有有限的用处。

    核心服务

    核心基础

    核心基础(CF)框架导出CFShow程序,该程序输出任何CF对象的描述到stderr。你可以在自己的代码中调用,然而,从GDB中调用时特别有用。清单8中展示了这样的一个例子。

    清单8 GDB调用CFShow 

    <p><span style="color:rgb(102,102,102);">$ gdb /Applications/TextEdit.app</span></p><p><span style="color:rgb(102,102,102);">GNU gdb 6.3.50-20050815 (Apple version gdb-1346) […]</span></p><p><span style="color:rgb(102,102,102);">(gdb) fb CFRunLoopAddSource</span></p><p><span style="color:rgb(102,102,102);">Breakpoint 1 at 0x624dd2f195cfa8</span></p><p><span style="color:rgb(102,102,102);">(gdb) r</span></p><p><span style="color:rgb(102,102,102);">Starting program: /Applications/TextEdit.app/Contents/MacOS/TextEdit </span></p><p><span style="color:rgb(102,102,102);">Reading symbols for shared libraries […]</span></p><p><span style="color:rgb(102,102,102);">Breakpoint 1, 0x00007fff8609bfa8 in CFRunLoopAddSource ()</span></p><p><span style="color:rgb(102,102,102);">(gdb) # Check that the prologue hasn't changed $rdi.</span></p><p><span style="color:rgb(102,102,102);">(gdb) p/a 0x00007fff8609bfa8</span></p><p><span style="color:rgb(102,102,102);">$1 = 0x7fff8609bfa8 <CFRunLoopAddSource+24></span></p><p><span style="color:rgb(102,102,102);">(gdb) p/a $pc</span></p><p><span style="color:rgb(102,102,102);">$2 = 0x7fff8609bfa8 <CFRunLoopAddSource+24></span></p><p><span style="color:rgb(102,102,102);">(gdb) x/8i $pc-24</span></p><p><span style="color:rgb(102,102,102);">0x7fff8609bf90 <CFRunLoopAddSource>: push   %rbp</span></p><p><span style="color:rgb(102,102,102);">0x7fff8609bf91 <CFRunLoopAddSource+1>: mov    %rsp,%rbp</span></p><p><span style="color:rgb(102,102,102);">0x7fff8609bf94 <CFRunLoopAddSource+4>: mov    %rbx,-0x20(%rbp)</span></p><p><span style="color:rgb(102,102,102);">0x7fff8609bf98 <CFRunLoopAddSource+8>: mov    %r12,-0x18(%rbp)</span></p><p><span style="color:rgb(102,102,102);">0x7fff8609bf9c <CFRunLoopAddSource+12>: mov    %r13,-0x10(%rbp)</span></p><p><span style="color:rgb(102,102,102);">0x7fff8609bfa0 <CFRunLoopAddSource+16>: mov    %r14,-0x8(%rbp)</span></p><p><span style="color:rgb(102,102,102);">0x7fff8609bfa4 <CFRunLoopAddSource+20>: sub    $0x40,%rsp</span></p><p><span style="color:rgb(102,102,102);">0x7fff8609bfa8 <CFRunLoopAddSource+24>: mov    %rdi,%r12</span></p><p><span style="color:rgb(102,102,102);">(gdb) # Nope. Go ahead and CFShow it.</span></p><p><span style="color:rgb(102,102,102);">(gdb) call (void)CFShow($rdi)</span></p><p><span style="color:rgb(102,102,102);"><CFRunLoop 0x100115540 [0x7fff70b8bf20]>{</span></p><p><span style="color:rgb(102,102,102);">    locked = false, </span></p><p><span style="color:rgb(102,102,102);">    wakeup port = 0x1e07, </span></p><p><span style="color:rgb(102,102,102);">    stopped = false,</span></p><p><span style="color:rgb(102,102,102);">    current mode = (none),</span></p><p><span style="color:rgb(102,102,102);">    common modes = <CFBasicHash 0x1001155a0 [0x7fff70b8bf20]>{</span></p><p><span style="color:rgb(102,102,102);">        type = mutable set, </span></p><p><span style="color:rgb(102,102,102);">        count = 1,</span></p><p><span style="color:rgb(102,102,102);">        entries =></span></p><p><span style="color:rgb(102,102,102);">            2 : <CFString 0x7fff70b693d0 [0x7fff70b8bf20]>{</span></p><p><span style="color:rgb(102,102,102);">                contents = "kCFRunLoopDefaultMode"</span></p><p><span style="color:rgb(102,102,102);">            }</span></p><p><span style="color:rgb(102,102,102);">    },</span></p><p><span style="color:rgb(102,102,102);">    common mode items = (null),</span></p><p><span style="color:rgb(102,102,102);">    modes = <CFBasicHash 0x1001155d0 [0x7fff70b8bf20]>{</span></p><p><span style="color:rgb(102,102,102);">        type = mutable set, </span></p><p><span style="color:rgb(102,102,102);">        count = 1,</span></p><p><span style="color:rgb(102,102,102);">        entries =></span></p><p><span style="color:rgb(102,102,102);">            0 : <CFRunLoopMode 0x100115670 [0x7fff70b8bf20]>{</span></p><p><span style="color:rgb(102,102,102);">                name = kCFRunLoopDefaultMode, </span></p><p><span style="color:rgb(102,102,102);">                locked = false, </span></p><p><span style="color:rgb(102,102,102);">                port set = 0x1f03,</span></p><p><span style="color:rgb(102,102,102);">                sources = (null),</span></p><p><span style="color:rgb(102,102,102);">                observers = (null),</span></p><p><span style="color:rgb(102,102,102);">                timers = (null)</span></p><p><span style="color:rgb(102,102,102);">        },</span></p><p><span style="color:rgb(102,102,102);">    }</span></p><p><span style="color:rgb(102,102,102);">}</span></p>


    重要:如果你没有看到CFShow输出任何东西,很可能是发送到控制台。如何查看输出,可参阅“查看调试输出信息”

    注意:在清单8中,CFShow的输出已经格式化,更容易阅读。

    GCB调用有很多其他CF程序,你会发现非常有用,包括CFGetRetainCountCFBundleGetMainBundle,和 CFRunLoopGetCurrent

    zombie

    重要:如果你用Objective-C编程,你可能对NSZombieEnabled更感兴趣,如更多zombie所述。

    核心基础支持叫做CFZombieLevel的环境变量。该变量为包含一组标志位的正式。表7描述了当前定义的bit。可以帮助你追踪各种CF内存管理问题。

    表7 CFZombieLevel 环境变量的bit定义

    Bit

    Action

    0

    释放CF内存

    1

    当释放CF内存,不要乱想对象头(CFRuntimeBase)

    4

    没有空闲内存用于保存CF对象

    7

    如果设置,使用bit8..15释放内存,否则使用0xFC

    8..15

    如果设置bit 7,使用这个值来释放内存

    16

    分配CF内存

    23

    如果设置,使用bit 24..31分配内存,否则使用0xCF

    24..31

    如果设置为23bit,使用这个值分配内存

    应用程序服务

    核心动画

    核心动画工具可以衡量应用程序的帧速率,查看各种类型的绘画。详情查看工具用户指南(Instruments User Guide)。

    Cocoa and Cocoa Touch

    所有Cocoa对象(一切均源自NSObject)支持description 方法,该方法返回一个NSString 描述对象。访问这个描述最方便的方法是通过Xcode打印描述到控制台。或者,如果你是个命令行迷,你可以使用GDBprint-object 命令,如清单9所示。

    清单9 使用GDB的命令

    <p><span style="color:rgb(102,102,102);">$ gdb /Applications/TextEdit.app</span></p><p><span style="color:rgb(102,102,102);">GNU gdb 6.3.50-20050815 (Apple version gdb-1346) […]</span></p><p><span style="color:rgb(102,102,102);">(gdb) fb -[NSCFDictionary copyWithZone:]</span></p><p><span style="color:rgb(102,102,102);">Breakpoint 1 at 0x83126e97675259</span></p><p><span style="color:rgb(102,102,102);">(gdb) r</span></p><p><span style="color:rgb(102,102,102);">Starting program: /Applications/TextEdit.app/Contents/MacOS/TextEdit </span></p><p><span style="color:rgb(102,102,102);">Reading symbols for shared libraries […]</span></p><p><span style="color:rgb(102,102,102);">Breakpoint 1, 0x00007fff837aa259 in -[NSCFDictionary copyWithZone:] ()</span></p><p><span style="color:rgb(102,102,102);">(gdb) po $rdi</span></p><p><span style="color:rgb(102,102,102);">{</span></p><p><span style="color:rgb(102,102,102);">    AddExtensionToNewPlainTextFiles = 1;</span></p><p><span style="color:rgb(102,102,102);">    AutosaveDelay = 30;</span></p><p><span style="color:rgb(102,102,102);">    CheckGrammarWithSpelling = 0;</span></p><p><span style="color:rgb(102,102,102);">    CheckSpellingWhileTyping = 1;</span></p><p><span style="color:rgb(102,102,102);">    […]</span></p><p><span style="color:rgb(102,102,102);">}</span></p>


    Objective-C

    在一个Objective-C异常处中断,不管它如何抛出异常,在objc_exception_throw设置一个象征性的断点。最简单的方法是在Xcode中设置断点,停止在Objective-C异常菜单命令。

    注意:比在-[NSException raise]设置断点更好,因为即使使用@throw抛出异常,断点仍会触发。

    Objective-C汇编级程序调试

    在汇编级别调试Cocoa代码时,请记住Objective-C运行时如下特性:

    Objective-C编译器为每个方法增加了两个隐式参数,第一个参数是一个指向调用对象(self)。

    第二个隐式参数是方法选择器(_cmd)。在Objective-C中这是SEL类型,在GDB中你可以打印C字符串一样打印它。

    Objective-C运行时调度方法时通过C函数的一种。最常见的是objc_msgSend,但有一些架构使用objc_msgSend_stret方法返回结构,另一些架构使用objc_msgSend_fpret方法返回浮点值。调用superobjc_msgSendSuper等等)都是等效函数。

    Objective-C对象的第一个单词(isa字段)是一个指向对象类的指针。

    注意:如果你对 Objective-C消息调度有兴趣,可以查看这篇文章。

    表8 总结了如何从GDB访问self_cmd,如果你停在方法的第一个指令。详情,可阅读‘Some Assembly Required’。

    表8 访问self_cmd

    Architecture

    self

    _cmd

    ARM

    $r0

    $r1

    Intel 32-bit

    *(int*)($esp+4)

    *(int*)($esp+8)

    清单10 展示了如何从使用GDB信息的例子

    重要:清单10和清单11都是在Mac OS X上创建,但核心技术在iOS上同样有用。有关如何转化这些技术到iOS,可以阅读‘Some Assembly Required for information ’。

    清单10 Objective-C运行时‘秘密’

    <p><span style="color:rgb(102,102,102);">$ gdb /Applications/TextEdit.app</span></p><p><span style="color:rgb(102,102,102);">GNU gdb 6.3.50-20050815 (Apple version gdb-1346) […]</span></p><p><span style="color:rgb(102,102,102);">(gdb) # Give the runtime a chance to start up.</span></p><p><span style="color:rgb(102,102,102);">(gdb) fb NSApplicationMain</span></p><p><span style="color:rgb(102,102,102);">Breakpoint 1 at 0x9374bc69df7307</span></p><p><span style="color:rgb(102,102,102);">(gdb) r</span></p><p><span style="color:rgb(102,102,102);">Starting program: /Applications/TextEdit.app/Contents/MacOS/TextEdit </span></p><p><span style="color:rgb(102,102,102);">Reading symbols for shared libraries […]</span></p><p><span style="color:rgb(102,102,102);">Breakpoint 1, 0x00007fff841a0307 in NSApplicationMain ()</span></p><p><span style="color:rgb(102,102,102);">(gdb) # Set a breakpoint on -retain.</span></p><p><span style="color:rgb(102,102,102);">(gdb) b *'-[NSObject(NSObject) retain]'</span></p><p><span style="color:rgb(102,102,102);">Breakpoint 2 at 0x7fff8608a860</span></p><p><span style="color:rgb(102,102,102);">(gdb) c</span></p><p><span style="color:rgb(102,102,102);">Continuing.</span></p><p><span style="color:rgb(102,102,102);"> </span></p><p><span style="color:rgb(102,102,102);">Breakpoint 2, 0x00007fff8608a860 in -[NSObject(NSObject) retain] ()</span></p><p><span style="color:rgb(102,102,102);">(gdb) # Hit the breakpoint; dump the first 4 words of the object</span></p><p><span style="color:rgb(102,102,102);">(gdb) x/4xg $rdi</span></p><p><span style="color:rgb(102,102,102);">0x1001138f0: 0x00007fff7055e6d8  0x0041002f01a00000</span></p><p><span style="color:rgb(102,102,102);">0x100113900: 0x0069006c00700070  0x0069007400610063</span></p><p><span style="color:rgb(102,102,102);">(gdb) # Now print the selector</span></p><p><span style="color:rgb(102,102,102);">(gdb) x/s $rsi</span></p><p><span style="color:rgb(102,102,102);">0x7fff848d73d8: "retain"</span></p><p><span style="color:rgb(102,102,102);">(gdb) # Want to 'po' object; must disable the breakpoint first</span></p><p><span style="color:rgb(102,102,102);">(gdb) dis</span></p><p><span style="color:rgb(102,102,102);">(gdb) po $rdi</span></p><p><span style="color:rgb(102,102,102);">/Applications/TextEdit.app</span></p><p><span style="color:rgb(102,102,102);">(gdb) # Print the 'isa' pointer, which is a Class object.</span></p><p><span style="color:rgb(102,102,102);">(gdb) po 0xa02d9740</span></p><p><span style="color:rgb(102,102,102);">NSPathStore2</span></p>


    无标识调试时,你可以使用Objective-C运行时函数来辅助你的调试工作,表9中展示的程序特别有用

    表9 有用的Objective-C运行时函数

    Function

    Summary

    id objc_getClass(const char *name);

    获取给定类名的Objective-C类对象

    SEL sel_getUid(const char *str);

    获取给定方法名的Objective-C SEL

    IMP class_getMethodImplementation(Class cls, SEL name);

    获取指向给定类给定方法的实现代码的指针

    清单11 展示了TextEdit的-[DocumentController openUntitledDocumentAndDisplay:error:] 方法的调试,尽管TextEdit没有标识。

    清单11 使用Objective-C运行时来无标识调试

    <p><span style="color:rgb(102,102,102);">$ gdb -arch x86_64 /Applications/TextEdit.app</span></p><p><span style="color:rgb(102,102,102);">GNU gdb 6.3.50-20050815 (Apple version gdb-1346) […]</span></p><p><span style="color:rgb(102,102,102);">(gdb) r</span></p><p><span style="color:rgb(102,102,102);">Starting program: /Applications/TextEdit.app/Contents/MacOS/TextEdit </span></p><p><span style="color:rgb(102,102,102);">Reading symbols for shared libraries […]</span></p><p><span style="color:rgb(102,102,102);">^C</span></p><p><span style="color:rgb(102,102,102);">(gdb) # Try to find the </span></p><p><span style="color:rgb(102,102,102);">(gdb) # -[DocumentController openUntitledDocumentAndDisplay:error:] </span></p><p><span style="color:rgb(102,102,102);">(gdb) # symbol.</span></p><p><span style="color:rgb(102,102,102);">(gdb) info func openUntitledDocumentAndDisplay</span></p><p><span style="color:rgb(102,102,102);">All functions matching regular expression "openUntitledDocumentAndDisplay":</span></p><p><span style="color:rgb(102,102,102);"> </span></p><p><span style="color:rgb(102,102,102);">Non-debugging symbols:</span></p><p><span style="color:rgb(102,102,102);">0x00007fff843ac083 -[NSDocumentController openUntitledDocumentAndDisplay:error:]</span></p><p><span style="color:rgb(102,102,102);">(gdb) # These are not the droids we're looking for. It turns out that </span></p><p><span style="color:rgb(102,102,102);">(gdb) # TextEdit ships with its symbols stripped, so we'll have to do </span></p><p><span style="color:rgb(102,102,102);">(gdb) # this the hard way.</span></p><p><span style="color:rgb(102,102,102);">(gdb) #</span></p><p><span style="color:rgb(102,102,102);">(gdb) # Get the Class object for the DocumentController class.</span></p><p><span style="color:rgb(102,102,102);">(gdb) set $class=(void *)objc_getClass("DocumentController")</span></p><p><span style="color:rgb(102,102,102);">(gdb) # Get the SEL object for the "openUntitledDocumentAndDisplay:error:" method.</span></p><p><span style="color:rgb(102,102,102);">(gdb) set $sel=(void *)sel_getUid("openUntitledDocumentAndDisplay:error:")</span></p><p><span style="color:rgb(102,102,102);">(gdb) # Get a pointer to the method implementation.</span></p><p><span style="color:rgb(102,102,102);">(gdb) call (void*)class_getMethodImplementation($class, $sel)</span></p><p><span style="color:rgb(102,102,102);">$1 = (void *) 0x100001966</span></p><p><span style="color:rgb(102,102,102);">(gdb) # Confirm that this is sensible. Looks like a method prologue to me.</span></p><p><span style="color:rgb(102,102,102);">(gdb) x/4i 0x00009aa5</span></p><p><span style="color:rgb(102,102,102);">0x100001966: push   %rbp</span></p><p><span style="color:rgb(102,102,102);">0x100001967: mov    %rsp,%rbp</span></p><p><span style="color:rgb(102,102,102);">0x10000196a: push   %r12</span></p><p><span style="color:rgb(102,102,102);">0x10000196c: push   %rbx</span></p><p><span style="color:rgb(102,102,102);">(gdb) # Set a breakpoint on the method.</span></p><p><span style="color:rgb(102,102,102);">(gdb) b *0x100001966</span></p><p><span style="color:rgb(102,102,102);">Breakpoint 1 at 0x100001966</span></p><p><span style="color:rgb(102,102,102);">(gdb) # Resume execution, and then create a new, untitled document.</span></p><p><span style="color:rgb(102,102,102);">(gdb) c</span></p><p><span style="color:rgb(102,102,102);">Continuing.</span></p><p><span style="color:rgb(102,102,102);">[…]</span></p><p><span style="color:rgb(102,102,102);">Breakpoint 1, 0x0000000100001966 in ?? ()</span></p><p><span style="color:rgb(102,102,102);">(gdb) # We've hit our breakpoint; print the parameters, starting with </span></p><p><span style="color:rgb(102,102,102);">(gdb) # the implicit "self" and "SEL" parameters that are common to all </span></p><p><span style="color:rgb(102,102,102);">(gdb) # methods, followed by the method-specific "display" and </span></p><p><span style="color:rgb(102,102,102);">(gdb) # "error" parameters.</span></p><p><span style="color:rgb(102,102,102);">(gdb) po $rdi</span></p><p><span style="color:rgb(102,102,102);"><DocumentController: 0x100227a50></span></p><p><span style="color:rgb(102,102,102);">(gdb) x/s $rsi</span></p><p><span style="color:rgb(102,102,102);">0x7fff848e4e04: "openUntitledDocumentAndDisplay:error:"</span></p><p><span style="color:rgb(102,102,102);">(gdb) p (int)$rdx</span></p><p><span style="color:rgb(102,102,102);">$2 = 1</span></p><p><span style="color:rgb(102,102,102);">(gdb) x/xg $rcx</span></p><p><span style="color:rgb(102,102,102);">0x7fff5fbff108: 0x00000001001238f0</span></p>


    你可以通过/usr/include/objc/的头,了解更多关于Objective-C 运行时函数与数据结构

    警告:Objective-C 运行时允许你发现更多系统类的私有实现细节。你在最终的产品中不能使用任何有关信息。

    Foundation

    Foundation有大量的调试工具,这些工具可以通过环境变量启用。表10中突出显示了这些变量。

    表10 Foundation 环境变量

    Name

    Default

    Action

    NSZombieEnabled

    NO

    如果设置为YES,已释放对象是'zombified' ,在你发送消息到已释放的对象时可以快速的调试问题。详情查阅更多Zombies

    NSDeallocateZombies

    NO

    如果设置为YES,已'zombified' 的对象的内存实际上是释放掉的

    NSUnbufferedIO

    NO

    如果设置为YESFoundation 将为stdoutstderr默认无缓冲)使用无缓冲 I/O

    重要:启用或禁用基础调试工具,你必须设置环境变量值为“是”或“否”,而非10,其他系统组件也一样。

    Retain 计数

    你可以使用retain计数来获取当前对象的retain计数。虽然有时候这种方法是有用的调试助手,当你解释结果时要非常小心。清单12展示了一个混乱的潜在来源。

    清单12 混乱的retain计数

    <p><span style="color:rgb(102,102,102);">(gdb) set $s=(void *)[NSClassFromString(@"NSString") string]</span></p><p><span style="color:rgb(102,102,102);">(gdb) p (int)[$s retainCount]</span></p><p><span style="color:rgb(102,102,102);">$4 = 2147483647</span></p><p><span style="color:rgb(102,102,102);">(gdb) p/x 2147483647</span></p><p><span style="color:rgb(102,102,102);">$5 = 0x7fffffff</span></p><p><span style="color:rgb(102,102,102);">(gdb) # The system maintains a set of singleton strings for commonly </span></p><p><span style="color:rgb(102,102,102);">(gdb) # used values, like the empty string. The retain count for these </span></p><p><span style="color:rgb(102,102,102);">(gdb) # strings is a special value indicating that the object can't be </span></p><p><span style="color:rgb(102,102,102);">(gdb) # released.</span></p>


    另一个常见混淆的来源时自动释放机制。如果一个对象呗自动释放,它的retain数可能比你想象的要高。在未来的某个时间点自动释放池将释放它。

    你可以决定哪些对象在哪个自动释放池中,通过调用 _CFAutoreleasePoolPrintPools 可以打印所有在指定释放池堆栈上的自动释放池的内容。

    清单13 打印自动释放池堆栈

    <p><span style="color:rgb(102,102,102);">(gdb) call (void)_CFAutoreleasePoolPrintPools()</span></p><p><span style="color:rgb(102,102,102);">- -- ---- -------- Autorelease Pools -------- ---- -- -</span></p><p><span style="color:rgb(102,102,102);">==== top of stack ================</span></p><p><span style="color:rgb(102,102,102);">  0x327890 (NSCFDictionary)</span></p><p><span style="color:rgb(102,102,102);">  0x32cf30 (NSCFNumber)</span></p><p><span style="color:rgb(102,102,102);">  […]</span></p><p><span style="color:rgb(102,102,102);">==== top of pool, 10 objects ================</span></p><p><span style="color:rgb(102,102,102);">  0x306160 (__NSArray0)</span></p><p><span style="color:rgb(102,102,102);">  0x127020 (NSEvent)</span></p><p><span style="color:rgb(102,102,102);">  0x127f60 (NSEvent)</span></p><p><span style="color:rgb(102,102,102);">==== top of pool, 3 objects ================</span></p><p><span style="color:rgb(102,102,102);">- -- ---- -------- ----------------- -------- ---- -- -</span></p>


    注意:_CFAutoreleasePoolPrintPools只适用于iOS4.0及更高版本。

    在早期系统,你可以使用函数来确定一个对象呗条件到自动释放池的次数。例如,见清单14.

    清单14 调用NSAutoreleasePoolCountForObject

    <p><span style="color:rgb(102,102,102);">(gdb) # d is an NSDictionary created with -[NSDictionary dictionaryWithObjectsAndKeys:].</span></p><p><span style="color:rgb(102,102,102);">(gdb) p d</span></p><p><span style="color:rgb(102,102,102);">$1 = (NSDictionary *) 0x12d620</span></p><p><span style="color:rgb(102,102,102);">(gdb) po d</span></p><p><span style="color:rgb(102,102,102);">{</span></p><p><span style="color:rgb(102,102,102);">    test = 12345;</span></p><p><span style="color:rgb(102,102,102);">}</span></p><p><span style="color:rgb(102,102,102);">(gdb) p (int)[d retainCount]</span></p><p><span style="color:rgb(102,102,102);">$2 = 1</span></p><p><span style="color:rgb(102,102,102);">(gdb) p (int)NSAutoreleasePoolCountForObject(d)</span></p><p><span style="color:rgb(102,102,102);">$3 = 1</span></p>


    更多的zombie

    用Cocoa编程时常见的bug类型是过度释放对象。这通常会导致应用程序崩溃,但当崩溃发生时最后一个引用计数会释放(当你试图给已释放对象发消息),通常会从原bug移除。对于调试这样的问题,NSZombieEnabled 是最好的选择。它将展示任何试图与释放对象交互。

    启用zombie最好的方法是通过工具。然而,你可以可以通过一个环境变量来启用zombie。清单15展示了在这种情况下你会看到的消息类型。

    清单15 NSZombie的效果

    $ NSZombieEnabled=YES build/Debug/DummyMac.app/Contents/MacOS/DummyMac  […] -[AppDelegate testAction:] […] *** -[CFNumber releas


    如清单15所示,如果系统检测到zombie,系统将执行一个断点指令。如果你在GDB下运行,程序将停止,你可以查看backtrace 来找出触发zombie检查器的调用链。

    NSZombieEnabled 也影响核心基础对象以及Objective-C对象。然而,只有当你从Objective-C访问一个zombie核心基础对象时你会被通知,而不是当你通过一个CF API来访问它。

    其他Foundation

    如果你使用键值观察并且你想知道谁在观察一个特定的对象,你可以使用GDB打印对象命令来获取对象的观察信息并将它打印。清单16给出了一个这样的例子。

    清单16 显示键值观察者

    (gdb) # self is some Objective-C object.
    (gdb) po self
    <ZoneInfoManager: 0x48340d0>
    (gdb) # Let's see who's observing what key paths.
    (gdb) po [self observationInfo]
    <NSKeyValueObservationInfo 0x48702d0> (
    <NSKeyValueObservance 0x4825490: Observer: 0x48436e0, \
    Key path: zones, Options: <New: NO, Old: NO, Prior: NO> \
    Context: 0x0, Property: 0x483a820>
    )


    你可以设置NSShowNonLocalizedStrings 的偏好来找到该被本地化但没有的字符串。一旦启用,如果你请求一个本地字符串同时在字符串文件中没找到该字符串,系统将返回大写的字符串和日志消息到控制台。这是一个很好的方法来发现过期本地化问题。

    这个设置会影响NSLocalizedString、所有变形以及包括CFCopyLocalizedString的底层基础结构。

    UIKit

    UIView实现了一个很有用的description 方法。此外,它还实现了recursiveDescription 方法,你可以调用这个方法获取整个视图层次。

    清单17 UIViewdescription 和recursiveDescription 方法

    -- The following assumes that you're stopped in some view controller method.
    (gdb) po [self view]
    <UIView: 0x6a107c0; frame = (0 20; 320 460); autoresize = W+H; layer = […]
    Current language: auto; currently objective-c
    (gdb) po [[self view] recursiveDescription]
    <UIView: 0x6a107c0; frame = (0 20; 320 460); autoresize = W+H; layer = […]
       | <UIRoundedRectButton: 0x6a103e0; frame = (124 196; 72 37); opaque = NO; […]
       |    | <UIButtonLabel: 0x6a117b0; frame = (19 8; 34 21); text = 'Test'; […]


    网络

    调试网络代码最重要的工具是packet trace。“获取到一个packet trace”( Technical Q&A QA1176, 'Getting a Packet Trace' )中讨论了如何在 Mac OS X上获取一个packet trace。

    尽管iOS没有内置packet trace工具,通常你可以通过Mac来获取packet trace。

    电源

    能源诊断工具是理解你的应用程序如何影响电池寿命的好方法。详情查看工具用户指南(Instruments User Guide )。

    推送通知

    “推送通知故障排除”(Technical Note TN2265, 'Troubleshooting Push Notifications' )是调试推送通知问题的全面指南。它还包括一个配置概要文件(APNsLogging.mobileconfig),你可以安装这个文件启用更多的调试日志。

    iPhone模拟器

    iPhone模拟器让你可以在Mac OS X上构建和运行iOS代码。因为iPhone模拟器是一个Mac OS X应用程序,你可以使用各种各样的Mac OS X调试工具。例如,如果你有个问题只能在DTrace (它在iOS上不可用)上解决,你可以在模拟器运行你的程序,并将DTrace 指向它。

    重要:模拟器很是和调试,但最终还是真正的设备决定是否在iOS可用。在做性能测试与调试的时候,一定要牢记这一点。

    关于Mac OS X 调试工具的更多信息,参见“Mac OS X 调试”(Technical Note TN2124, 'Mac OS X Debugging Magic')。

    文件修订历史

     

    Date

    Notes

    2011-01-22

    描述大量的iOS调试提示与技巧的新文档


    官方原文地址:

    https://developer.apple.com/library/ios/technotes/tn2239/_index.html


    展开全文
  • 如何用windows电脑+ios调试手机上打开的网页 一、环境 PC chrome浏览器(版本45、46) IOS safari浏览器(11.2.6) win系统(win7、win10) 一根USB数据线 二、打开iphone上 Safari浏览器的web检查器 设置 --&...
  • 开发PC页面的时候使用chrome浏览器的开发者工具,可以很容易的捕获到页面的dom元素,并且可以修改样式,方便调试,但是手机上却很麻烦,因为手机上没有办法直接打开开发者工具查看元素。其实可以通过将设备连接到PC...
  • 本文介绍如何在 Windows 系统中连接 iOS设备 并对 Web 页面进行真机调试 必须前提 iOS设备、数据线 Node.js 环境 Chrome 浏览器 环境准备 安装Node环境 参考Node安装的教程,确保终端输入node时可正常使用 ...
  • iOS13.4.1真机调试包.zip

    2020-07-29 11:28:41
    适用于iOS13.4.1真机调试,亲测可用,用法打开路径: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport将解压缩的文件夹放进去重启Xcode即可
  • android平台有直接用chrome beta就可以调试,具体操作办法可以...那么用pc如何在ios上实时调试我们的移动版主题呢?weinre?麻烦了点儿,本文将为您着重介绍一款国产神器MIHTool,说它是神器,一点都为过,嗯?...
  • 1、ios手机连接到pc设置手机safari 设置-》safari-》高级-》打开web检查器 (授权调试功能)2.、安装ios_webkit_debug_proxy根据文档,在windows下,首先需要安装scoop,而安装scoop需要电脑里有powershell。win10...
  • 相对于一些应用,我本人对ios游戏开发更感兴趣,这些开发总结并拘泥于技术,更多的探讨了开发过程。过程很重要,过程正确才能保证结果正确,没有正确的过程,一切方法都是纸上谈兵。 听听Thomas Henshe
  • 网上找了很多资料,最后确定是前端JS代码使用了ES6版本的语法,而IOS11以下和微信PC版是支持ES6语法的,所以需要降低语法到ES5。前端人员在打包的时候进行了转换,但是还是无法解决问题。我们对打包后的文件再次...
  • 如果有一个app是用的OS提供的webview,那么我们可以像PC那样调试web一样调试app的webview。 iOS的app可以用Safari浏览器来进行远程调试,步骤如下: 1 将iphone用USB线连接到电脑上。 2 打开Safari浏览器, 点击...
  • 通过iOS设备控制PC可能较为常见,App Store也有不少类似的应用,但是通过PC控制iOS相信大家很难在网上找到解决方案,找到的也大部分是需要依赖越狱来实现。 安卓提供了强大的adb工具,轻松实现类似的功能。但...
  • userPlatform() { if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) { //判断iPhone|iPad|iPod|iOS return 'ios' } else if (/(Android)/i.test(navigator.userAgent)) { //判断Android...
  • iOS模拟器调试WebView

    2018-11-08 14:35:10
    首先需要安装 Xcode,安装完毕后,在终端中运行 xcode-select --install安装 Command-line-tools。 安装完毕后搜索Simulator,或者按 Command+Shift+G, 输入/Applications/Xcode.app/Contents/Developer/...
  • U3D编译Web PC IOS Android平台游戏和运行方法 一、U3D游戏编译介绍: U3D做为一款支持多平台编译开发的游戏引擎,相比较其他的游戏开发引擎而言,可谓强大,而Mono是其能够实现跨平台的核心。 打开Editor菜单...
  • U3D中的Profile也是可以直接在链接安卓设备运行游戏下查看的,导出真机链接U3D的Profile看数据,这样更好的测试具体原因。 大概看了下官方的做法,看了几张帖子顺带把做法记录下来。 参考:...
  • Profile是unity内置的很重要的性能查看工具,而且支持真机调试模式。也就是可以在Unity的编辑器模式下看到移动设备上运行的App的情况。需要说明的是查看安卓下profile可以在Windows操作系统下即可,但查看ios的就.....
1 2 3 4 5 ... 20
收藏数 18,879
精华内容 7,551
关键字:

pc能不能调试ios