【问题标题】:Beyond Stack Sampling: C++ Profilers超越堆栈采样:C++ Profilers
【发布时间】:2011-05-22 15:08:18
【问题描述】:

黑客的故事

日期是 2010 年 12 月 2 日。圣诞节前的日子一天天过去,作为一名 Windows 程序员,我几乎遇到了重大障碍。我一直在使用 AQTime,我试过昏昏欲睡,闪亮,非常困倦,正如我们所说,VTune 正在安装。我曾尝试使用 VS2008 分析器,它一直在受到积极的惩罚,而且常常是不理智的。我使用了随机暂停技术。我检查了调用树。我已经启动了函数跟踪。但令人悲痛的事实是,我正在使用的应用程序有超过一百万行代码,可能还有另外一百万行第三方应用程序。

我需要更好的工具。 我已经阅读了其他主题。我已经尝试了每个主题中列出的每个分析器。必须有比这些垃圾和昂贵的选择更好的东西,或者几乎没有收益的可笑工作量。更复杂的是,我们的代码是重线程的,并且运行了许多 Qt 事件循环,其中一些非常脆弱,以至于由于时间延迟而在繁重的仪器下崩溃。不要问我为什么要运行多个事件循环。没有人能告诉我。

在 Windows 环境中是否有更多类似于 Valgrind 的选项?
还有什么比我已经尝试过的大量损坏的工具更好的吗?
是否有任何设计用于与 Qt 集成,也许可以在队列中显示有用的事件?

我尝试过的工具的完整列表,其中真正有用的工具用斜体表示:

  • AQTime:相当不错!深度递归有一些问题,但调用图在这些情况下是正确的,可用于消除您可能遇到的任何困惑。不是一个完美的工具,但值得一试。它可能适合您的需求,而且在大多数情况下对我来说肯定已经足够了。
  • 调试模式下的随机暂停攻击:信息不足。
    一个很好的工具,但不是一个完整的解决方案。
  • Parallel Studios:核选项。突兀,怪异,疯狂的强大。我认为您应该进行 30 天评估,并确定它是否合适。这也太酷了。
  • AMD Codeanalyst:很棒,易于使用,非常容易崩溃,但我认为这是环境问题。我建议您尝试一下,因为它是免费的。
  • Luke Stackwalker: 在小型项目上运行良好,但在我们的项目上运行起来有点困难。虽然取得了一些不错的效果,但它绝对可以取代 Sleepy 来完成我的个人任务。
  • PurifyPlus: 不支持 Win-x64 环境,最突出的是 Windows 7。否则非常好。我在其他部门的许多同事都对此发誓。
  • VS2008 Profiler:在函数跟踪模式下以所需的分辨率产生 100+gigs 范围内的输出。从好的方面来说,会产生可靠的结果。
  • GProf:需要 GCC 才能达到中等效果。
  • VTune:VTune 的 W7 支持近乎犯罪。其他方面都很好
  • PIN:我需要破解自己的工具,所以这是最后的手段。
  • Sleepy\VerySleepy:适用于较小的应用程序,但我在这里失败了。
  • EasyProfiler:如果您不介意手动注入代码来指示检测位置,这还不错。
  • Valgrind:仅限 *nix,但在那种环境中非常好。
  • OProfile:仅限 Linux。
  • Prooffy:他们射野马。

我没有尝试过的建议工具:

  • XPerf:
  • Glowcode:
  • 开发伙伴:

注意事项: 目前的英特尔环境。 VS2008,提升库。 Qt 4+。以及它们中最糟糕的一个:通过 trolltech 进行的 Qt/MFC 集成。


现在:差不多两周后,看来我的问题已经解决了。借助各种工具,包括列表中的几乎所有内容以及我的一些个人技巧,我们找到了主要瓶颈。但是,我将继续测试、探索和尝试新的分析器以及新技术。为什么?因为我欠你们,因为你们摇滚。它确实会稍微减慢时间线,但我仍然很高兴能继续尝试新工具。

概要
在许多其他问题中,最近一些组件被切换到了不正确的线程模型,由于我们下面的代码突然不再是多线程的,导致严重的挂起。我不能说更多,因为它违反了我的 NDA,但我可以告诉你,这永远不会通过随意检查甚至正常的代码审查来发现。如果没有剖面仪、callgraphs 和随机停顿,我们仍然会对美丽的蓝色天空弧线大喊大叫。值得庆幸的是,我与一些我见过的最优秀的黑客一起工作,并且我可以接触到一个充满了伟大工具和伟大人物的令人惊叹的“诗句”。

各位大侠,我非常感激,但很遗憾我没有足够的声望来奖励你们每一个人。我仍然认为这是一个重要的问题,可以得到比我们迄今为止在 SO 上得到的更好的答案。

因此,在接下来的三周中,我每周都会提供我能负担得起的最大赏金,并用我认为不是常识的最好工具来奖励答案。三周后,如果您能原谅我的双关语,我们希望我们已经积累了分析器的明确概况。

外卖
使用分析器。它们对 Ritchie、Kernighan、Bentley 和 Knuth 来说已经足够好了。我不在乎你认为你是谁。使用分析器。如果你有的一个不起作用,那就找另一个。如果找不到,请编码一。如果您无法编写代码,或者这是一个小问题,或者您只是卡住了,请使用随机暂停。如果一切都失败了,请聘请一些研究生来做一个分析器。


更长远的观点
所以,我认为写一些回顾会可能会很好。我选择与 Parallel Studios 广泛合作,部分原因是它实际上是建立在 PIN 工具之上的。在与一些相关研究人员进行过学术交流后,我觉得这可能是某种品质的标志。谢天谢地,我是对的。虽然 GUI 有点可怕,但我发现 IPS 非常有用,尽管我不能轻松地向所有人推荐它。至关重要的是,没有明显的方法来获取行级命中计数,这是 AQT 和许多其他分析器提供的,我发现它对于检查分支选择率非常有用。在网络上,我也很喜欢使用 AQTime,而且我发现他们的支持非常灵敏。再次,我必须证明我的建议:他们的许多功能都不能很好地工作,其中一些在 Win7x64 上非常容易崩溃。 XPerf 的表现也令人钦佩,但对于在某些类型的应用程序上获得良好读取所需的采样细节而言,速度非常慢。

现在,我不得不说,我认为在 W7x64 环境中分析 C++ 代码没有明确的选项,但肯定有一些选项根本无法执行任何有用的服务。

【问题讨论】:

  • 您是否考虑过换一份工作? :)
  • 我还能去哪里解决这么难的谜题?我想我可以回到内核黑客领域,但这并不值得。
  • @Kos 我认为要使 gprof 有任何用途,您必须使用使用 -pg 编译的 gcc 工具集,否则它不会生成 gprof.out 文件。在 OP 的情况下,听起来他正在使用 msvc,这排除了使用 gprof。再说一次,如果名单上的其他人未能满足他的需求,我认为 gprof 对他来说不会更好
  • @Marc Gravell 这很公平,我想.... 这对我来说似乎是一个奇怪的启发式方法,维护得最好的帖子突然进入社区领域,有效地产生了一种情况,即更多您更新并维护您的问题或答案,在整个社区的眼中,您从维护中获得的越少。我应该把这个带到 meta 吗?
  • 鉴于我现在对分析器的了解,有人想要回顾展吗?

标签: c++ qt optimization profiling profiler


【解决方案1】:

我使用 Orbit profiler,简单、开源且功能强大! https://orbitprofiler.com/

【讨论】:

    【解决方案2】:

    只是把它扔掉,即使它不是一个成熟的分析器:如果你所追求的只是挂起需要长时间处理事件的事件循环,ad-hoc tool 在 Qt 中很简单。这种方法可以很容易地扩展,以跟踪每个事件需要多长时间来处理,以及这些事件是什么,等等。它不是一个通用的分析器,而是一个以事件循环为中心的分析器。

    在 Qt 中,所有跨线程信号槽调用都通过事件循环传递,定时器、网络和串行端口通知以及所有用户交互也是如此。因此,观察事件循环是了解应用程序在哪里花费时间的一大步。

    【讨论】:

      【解决方案3】:

      我使用 xperf/ETW 来满足我的所有分析需求。它有一个陡峭的学习曲线,但非常强大。如果您在 Windows 上进行分析,那么您必须了解 xperf。我经常使用这个分析器来查找我的代码和其他人的代码中的性能问题。

      在我使用它的配置中:

      • xperf 从每个执行代码的内核中抓取 CPU 样本 小姐。采样率可以提高到 8 KHz,采样 包括用户模式和内核代码。这允许找出什么是 线程在运行时正在做
      • xperf 记录每个上下文 开关(允许完美重建每个 线程使用),加上线程切换时的调用堆栈,加上 哪个线程准备另一个线程的调用堆栈,允许跟踪 等待链并找出线程未运行的原因
      • xperf 记录所有进程的所有文件 I/O
      • xperf 记录所有磁盘 I/O 来自所有进程
      • xperf 记录哪个窗口处于活动状态,CPU 频率、CPU 电源状态、UI 延迟等。
      • xperf 也可以记录所有 来自一个进程的堆分配,来自所有进程的所有虚拟分配 流程等等。

      这是一个时间线上所有流程的大量数据。 Windows 上没有其他分析器可以做到这一点。

      我在博客中广泛介绍了如何使用 xperf/ETW。这些博客文章和一些专业质量的培训视频可以在这里找到: http://randomascii.wordpress.com/2014/08/19/etw-training-videos-available-now/

      如果您想了解不使用 xperf 会发生什么情况,请阅读以下博客文章: http://randomascii.wordpress.com/category/investigative-reporting/ 这些是我在其他人的代码中发现的性能问题的故事,应该由开发人员发现。这包括加载到 VC++ 编译器中的 mshtml.dll、VC++ 的文件查找中的拒绝服务、数量惊人的客户计算机中的热限制、Visual Studio 中缓慢的单步执行、硬中的 4 GB 分配磁盘驱动程序、PowerPoint 性能错误等。

      【讨论】:

        【解决方案4】:

        如果您对事件循环持怀疑态度,是否可以覆盖 QCoreApplication::notify() 并进行一些手动分析(一到两个发送者/事件到计数/时间的映射)?

        我认为您首先记录事件类型的频率,然后更仔细地检查这些事件(哪个对象发送它,它包含什么等)。跨线程的信号是隐式排队的,因此它们最终会出现在事件循环中(显然,显式排队的连接也是如此)。

        我们这样做是为了在我们的事件处理程序中捕获和报告异常,所以真的,每个事件都会经过那里。

        只是一个想法。

        【讨论】:

        • 这是个好主意!我不习惯 QT 环境,到目前为止,我的大部分工作都是使用 pyGTK 完成的。谢谢!
        • 您有推荐的方法来获取和解决给定信号的性质吗?
        • 我只对带有 QStateMachine::SignalEvent 的信号进行了处理,这似乎不一样。源应该仍然是QObject* object 参数。也许 MetaCall 是所有信号的类型(似乎很可能),但我不确定。这有点超出了我的经验,但是查看 Qt 源代码可能会发现一些真相。 (或者,在 SO .. 上问一个更尖锐的问题 w.r.t. queued signal invocations :)
        【解决方案5】:

        我刚刚完成了CxxProf 的第一个可用版本,这是一个用于 C++ 的便携式手动检测分析库。

        它实现了以下目标:

        • 轻松集成
        • 在编译期间轻松删除 lib
        • 在运行时轻松移除库
        • 支持多线程应用程序
        • 支持分布式系统
        • 将影响降至最低

        这些点是从project wiki 中提取的,请查看那里了解更多详情。

        免责声明:我是 CxxProf 的主要开发者

        【讨论】:

          【解决方案6】:

          第一:

          时间采样分析器比 CPU 采样分析器更强大。我对 Windows 开发工具不是很熟悉,所以我不能说哪些是哪些。大多数分析器都是 CPU 采样。

          CPU 采样分析器每 N 条指令抓取一次堆栈跟踪。
          此技术将揭示受 CPU 限制的代码部分。如果那是您的应用程序的瓶颈,那就太棒了。如果您的应用程序线程大部分时间都在争夺互斥锁,那就不是很好了。

          时间采样分析器每 N 微秒抓取一次堆栈跟踪。
          这种技术将在 “慢” 代码上归零。原因是 CPU 限制、阻塞 IO 限制、互斥量限制还是代码的缓存抖动部分。简而言之,任何使您的应用程序变慢的代码都会脱颖而出。

          所以尽可能使用时间采样分析器,尤其是在分析线程代码时。

          第二:

          采样分析器会生成大量数据。这些数据非常有用,但通常有太多无法轻松使用的数据。配置文件数据可视化工具在这里有很大帮助。我发现的用于个人资料数据可视化的最佳工具是gprof2dot。不要让名字欺骗你,它处理各种采样分析器输出(AQtime、Sleepy、XPerf 等)。一旦可视化结果指出了有问题的函数,请跳回原始配置文件数据,以更好地了解真正的原因是什么。

          gprof2dot 工具会生成一个dot graph description,然后您将其输入graphviz 工具。输出基本上是一个调用图,其中函数根据它们对应用程序的影响进行颜色编码。

          让 gprof2dot 生成漂亮输出的一些提示。

          • 我在图表上使用了 0.001 的 --skew,因此我可以轻松查看热代码路径。否则,int main() 将主导图表。
          • 如果您对 C++ 模板做任何疯狂的事情,您可能需要添加 --strip。 Boost 尤其如此。
          • 我使用 OProfile 生成我的采样数据。为了获得良好的输出,我需要将其配置为从我的第 3 方和系统库中加载调试符号。请务必这样做,否则您会看到 CRT 占用了应用程序 20% 的时间,而真正发生的情况是 malloc 正在破坏堆并占用 15%。

          【讨论】:

          • 虽然我不知道这是我问题的完整答案,但 gprof2dot 已进入我庞大的武器库,并迅速占据了我最喜欢的位置。我认为这值得赏金!
          • 我问了这个问题Linux time sample based profiler。 OProfile 最终应该是基于获取时间的采样。它们产生非常高质量的输出,因此一旦添加了该功能,我就会使用它们。除此之外,我有一个朋友一起破解了一个用于分析的 gdb + backtrace 解决方案。非常hacky,但它确实找到了瓶颈。
          • @deft_code: “将一个 gdb + backtrace 解决方案组合在一起进行分析。非常 hacky,但它确实找到了瓶颈。” 你正在确认我不断的咆哮 :) 一些人们希望配置文件漂亮,but if results are what you need, go with what works, not what's pretty.
          • 我同意迈克·邓拉维的观点。像 XPerf/WPA 这样的东西看起来非常漂亮和强大,但是弄清楚如何使用这些工具需要一段时间,而且在一天结束时,随机暂停非常容易,并且可以提供更好的信息来解决问题。更自动化的解决方案似乎往往会过滤掉解决瓶颈所需的关键信息。
          • Visual Studio 在包装盒中附带了一个时间采样分析器。 docs.microsoft.com/en-us/visualstudio/profiling/…
          【解决方案7】:

          这里列出了很多分析器,我自己尝试了其中的一些 - 但我最终基于此编写了自己的:

          http://code.google.com/p/high-performance-cplusplus-profiler/

          它当然需要您修改代码库,但它非常适合缩小瓶颈,应该适用于所有 x86(可能是多核机器的问题,即它使用 rdtsc,但是 - 这纯粹是为了无论如何,指示性时间 - 所以我发现它足以满足我的需求..)

          【解决方案8】:

          虽然你的操作系统是win7,但程序不能在xp下运行? 在 xp 下配置文件怎么样,结果应该是 win7 的提示。

          【讨论】:

          • 当然可以,但这需要购买一个产品的许可证,该产品可能永远无法很好地支持您想要的开发环境,或者可能需要数年才能做到这一点。 1.5k 是一笔不小的赌注,再加上成像和部署 xp 盒子的时间成本。
          【解决方案9】:

          我可以告诉你我每天都用什么。

          a) AMD 代码分析师

          • 这很简单,它可以让您快速了解正在发生的事情。大部分时间都可以的。
          • 使用 AMD CPU,它会告诉您有关 cpu 管道的信息,但只有当您有大量循环时才需要此信息,例如图形引擎、视频编解码器等。

          b) VTune。

          • 很好地集成在vs2008中

          • 在了解热点之后,您不仅需要对时间进行采样,还需要对缓存未命中和内存使用情况等其他内容进行采样。这非常重要。设置采样会话,并编辑属性。我总是对时间、内存读/写和缓存未命中(三种不同的运行)进行采样

          但除了工具之外,您还需要获得分析方面的经验。这意味着了解 CPU/内存/PCI 的工作原理……所以,这是我的第三个选择

          c) 单元测试

          如果您正在开发需要巨大性能的大型应用程序,这一点非常重要。如果您无法将应用程序拆分为某些部分,则很难跟踪 cpu 使用情况。我没有测试所有案例和类,但我有硬编码的执行和具有重要功能的输入文件。

          我的建议是在几个小测试中使用随机抽样,并尝试标准化配置文件策略。

          【讨论】:

          • AMD Code Analyst 在我的开发环境中不稳定,VTune 明确不支持。 :S
          【解决方案10】:

          编辑:我现在看到您在第一篇文章中提到了这一点。该死,我从没想过我会成为那个人。

          您可以使用Pin 以更精细的粒度检测您的代码。我认为 Pin 可以让您创建一个工具来计算您输入函数的次数或在那里花费的时钟滴答数,大致模拟 VTune 或 CodeAnalyst 之类的东西。然后,您可以剥离哪些功能需要检测,直到您的时间问题消失。

          【讨论】:

          • 实际上,PIN 是我最先接触到的。实际上有一种叫做 PIN Play 的东西会很完美,但它不适合在英特尔之外发布。我不确定我是否记得足够多关于使用 PIN 将一些非常好的东西组合在一起,但是......
          【解决方案11】:

          我已经成功地将PurifyPlus 用于Windows。尽管它并不便宜,但 IBM 提供了一个稍微残废的试用版。使用 quantify 进行分析所需的只是 pdb 文件并使用 /FIXED:NO 链接。唯一缺点:不支持Win7/64。

          【讨论】:

          • 不幸的是,我们的主要目标是 Win7。我会将该信息添加到主帖中。
          • PurifyPlus当前版本支持Win7/64。
          【解决方案12】:

          结帐XPerf

          这是 MS 提供的免费、非侵入性和可扩展的分析器。它是由 Microsoft 开发的,用于分析 Windows。

          【讨论】:

            【解决方案13】:

            DevPartner 最初由 NuMega 开发,现在由 MicroFocus 分发,曾经是分析和代码分析(例如内存和资源泄漏)的首选解决方案。 我最近没有尝试过,所以我不能保证它会对你有所帮助;但我曾经用它取得了很好的结果,所以这是我考虑在我们的代码质量过程中重新安装的替代方案(他们提供 14 天的试用期)

            【讨论】:

              【解决方案14】:

              另外两个工具建议。

              Luke Stackwalker 有一个可爱的名字(即使它有点符合我的口味),它不会花费您任何费用,而且您可以获得源代码。它也声称支持多线程程序。所以肯定值得一试。

              http://lukestackwalker.sourceforge.net/

              还有 Glowcode,我曾向我指出值得使用:

              http://www.glowcode.com/

              很遗憾,我有一段时间没有做任何 PC 工作了,所以我没有尝试过其中任何一个。无论如何,我希望这些建议对您有所帮助。

              【讨论】:

                【解决方案15】:

                你有 MFC OnIdle 功能吗?过去,我有一个近乎实时的应用程序,我必须修复它在设置为 19.2K 速度时丢弃串行数据包,而 PentiumD 应该能够跟上。 OnIdle 函数正在扼杀一切。我不确定 QT 是否有这个概念,但我也会检查一下。

                【讨论】:

                • 我们确实有一个 OnIdle,并且由于我们的 QTMFC 集成,它通过 QT ev..e...eve...event 循环流动。哦,天哪。
                • 事实证明这直接导致了我们的解决方案,因此虽然它不是问题的完美答案,但我认为这个问题无法回答。
                【解决方案16】:

                Easyprofiler - 我还没有在这里看到它,所以不确定你是否已经看过它。它在收集指标数据的方式上略有不同。使用其编译时配置文件方法的一个缺点是您必须对代码库进行更改。因此,您需要了解可能出现慢的地方并在那里插入分析代码。

                不过,根据您最新的 cmets,听起来您至少在​​一些方面取得了进展。也许这个工具可能会为您提供一些有用的指标。如果没有别的,它有一些非常简单的图表和图片:P

                【讨论】:

                  【解决方案17】:

                  关于 VS Profiler - 如果它生成如此大的文件,也许您的采样间隔太频繁了?尝试降低它,因为无论如何您可能有足够的样本。

                  理想情况下,请确保在您真正锻炼问题区域之前不要收集样本。所以从暂停收集开始,让你的程序做它的“缓慢活动”,然后开始收集。您最多只需要 20 秒的收集时间。在此之后停止收集。

                  这应该有助于减少样本文件的大小,并且只捕获分析所需的内容。

                  【讨论】:

                  • 我明天试试这个。
                  【解决方案18】:

                  当您尝试随机暂停时发生了什么?我一直在怪物应用程序上使用它。您说它没有提供足够的信息,并且您建议您需要高分辨率。有时人们需要一点帮助来理解如何使用它。

                  我在 VS 下所做的是配置堆栈显示,因此它不会向我显示函数参数,因为这会使堆栈显示完全不可读,IMO。

                  然后我通过点击“暂停”来采集大约 10 个样本在它让我等待的时候。我用^A、^C和^V将它们复制到记事本中,以供参考。然后我研究每一个,试图弄清楚当时它在尝试完成的过程中是什么。

                  如果它试图在 2 个或更多样本上完成某些事情,而这并不是绝对必要的,那么我发现了一个实际问题,并且我大致知道修复它可以节省多少。

                  有些事情您并不真正需要知道,例如精确的百分比并不重要,而 3rd 方代码中发生的事情也不重要,因为您无法对这些做任何事情>。您可以做的就是在代码中您可以修改的丰富调用点集,这些调用点显示在每个堆栈示例上。那是你快乐的狩猎场。

                  我发现的各种事物的示例:

                  • 在启动过程中,在尝试从 DLL 资源中提取国际化字符串的过程中,它可能有 30 层左右的深度。如果检查实际字符串,很容易发现这些字符串并不真正需要进行国际化,就像它们是用户从未真正看到的字符串一样。

                  • 在正常使用期间,某些代码会无意中在某些对象中设置 Modified 属性。该对象来自一个超类,它捕获更改并触发通知,这些通知会波及整个数据结构,操纵 UI,以难以预见的方式创建和销毁对象。这种情况经常发生 - 通知的意外后果。

                  • 逐行、逐个单元格地填写工作表。事实证明,如果你从一组值一次构建所有行,它会快得多。

                  附:如果你是多线程的,当你暂停它时,所有线程都会暂停。看一下每个线程的调用栈。很有可能,其中只有一个是真正的罪魁祸首,而其他人则处于闲置状态。

                  【讨论】:

                  • 评论?注释?这是斯巴达!我...对不起,不知道从哪里来的。不,该代码使 Klingon Opera 看起来可读,而且它的文档也差不多。实际上,我认为它的记录要少得多....天哪。
                  • QTMFC 集成?哦,太好了,你已经complicatedevil了,你甚至还没有接触到特定于应用程序的代码。
                  • QT/MFC?这难道不应该产生具有 3 个来回摇摆的突变儿童,同时称他们听到的每一个想法都是有史以来最愚蠢的想法吗? Errr...我离题了...如果您使用任何 MFC Socket 类,请立即重写您的套接字代码,然后配置文件。 CSocket 代码中有很多地方使用 WaitForSingleObject 的消息循环版本,我发现这会降低性能。对于我的生活,我不记得等待函数的名称......:/
                  • 天哪,相信我,这和你想象的一样糟糕。
                  • @Jake:不是很舒服,但这就是图灵普遍性的荣耀。任何语言,无论其级别多高或低级,都具有被滥用的无限能力。
                  【解决方案19】:

                  我在AMD CodeAnalyst 上取得了一些成功。

                  【讨论】:

                  • 英特尔环境,目前。不过我会记住的! :)
                  • @Jake:我不确定你的意思。 AMD CodeAnalyst 不需要 AMD 芯片,它应该适用于大多数 x86 或 x64(又名 x86-64/IA-64/AMD64)芯片,包括 Intel 芯片。
                  • 显然,我是文盲!这是个好消息。明天我会尝试并更新问题。
                  • 到目前为止,在我需要的分辨率下采样时非常不稳定。
                  • @Adam:我最近在英特尔奔腾 IV 机器上尝试了代码分析,它只提供基于时间的采样,没有关于线程使用的信息,也没有任何与线程相关的信息......我得到的信息真的很平庸..另外它导致了visual studio的qt集成崩溃..我不满意:(
                  猜你喜欢
                  • 1970-01-01
                  • 2015-06-14
                  • 2014-10-16
                  • 2012-01-18
                  • 1970-01-01
                  • 2022-01-18
                  • 2017-02-17
                  • 1970-01-01
                  • 2014-05-12
                  相关资源
                  最近更新 更多