posts - 319, comments - 22, trackbacks - 0, articles - 11
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

http://www.greenxf.com/soft/24170.html

ExEinfo PE(可执行程序检查工具)V0.0.3.0 绿色版

exeinfo pe 是一款免费的win32可执行程序检查器,它可以检查程序的打包方式,exe保护等,可以帮助开发人员对程序进行破解

posted @ 2012-02-01 20:24 RTY 阅读(289) | 评论 (0)编辑 收藏

http://www.greenxf.com/soft/25434.html

将鼠标移动到您要探测的窗体或控件上,即可获取该窗体的句柄、类名、文本(包括密码文本)、窗口位置、窗口尺寸、程式路径、编程语言等信息。

posted @ 2012-02-01 20:22 RTY 阅读(351) | 评论 (0)编辑 收藏

http://www.greenxf.com/soft/27822.html

文件数字签名克隆工具[单文件版]V1.00 绿色版

可复制其他文件数字签名。

posted @ 2012-02-01 20:18 RTY 阅读(327) | 评论 (0)编辑 收藏

Building PySide on Microsoft Windows

Prerequisites

NOTE: Be sure that git.exe and cmake.exe are all in your PATH.

Build

  • Open “Visual Studio Command Prompt”: [Start Menu]->Programs->Microsoft Visual C++ 2008 Express Edition->Visual Studio Tools
  • Get build scripts from repository http://qt.gitorious.org/pyside/packaging and go to folder “c:\repositories\packaging\setuptools”. The script can automatically download the sources, compile them, and create the installer, all in one step.
  • Run the build.py script (it must be run from “Visual Studio Command Prompt”):
To build the latest stable binaries for Python 2.7 and Qt 4.7.3, run the script with parameters:
  1.   c:\repositories\packaging\setuptools>c:\Python27\python.exe build.py --q c:\Qt\4.7.3\bin\qmake.exe
  2.  
To build the latest development binaries:
  1.   c:\repositories\packaging\setuptools>c:\Python27\python.exe build.py --m dev -q c:\Qt\4.7.3\bin\qmake.exe
  2.  
All build.py parameters:
  1.    -<package_version> Specify package version. Default is latest stable version (1.0.4)
  2.    -d                   Download latest sources from git repository
  3.    -<pyside_version>  Specify what version of modules to download from git repository:
  4.                          'dev' (master tag) or 'stable' (1.0.4 tag)Default is 'stable'.
  5.    -<qmake_path>      Locate qmake
  6.    -e                   Check the environment
  7.    -b                   Specify what module to build
  8.    -o                   Create a distribution package only using existing binaries
  9.    
  • After the successful build, the final binary distribution can be found in sub-folder “dist”:
    1.   c:\repositories\packaging\setuptools\dist\PySide-1.0.4qt473.win32-py2.7.exe
    2.  

Categories:

posted @ 2012-01-14 21:08 RTY 阅读(435) | 评论 (0)编辑 收藏

Ubuntu Mobile Phone Concepts

By , Published December 4, 2011
Share:

Ubuntu’s plan for a multi-device presence, to potentially include smartphones, has fired up the imagination of users.

And the community have been quick to stoke their creative talents in mocking up a variety of possible implementations.

Below are three of those community designs that have landed in our inbox over the last week…

Community Designs

Nick Rutledge mailed in with a link to his initial designs which are more traditional in approach than some mobile mock-ups that have been bandied around.

ubuntu mobile designs

nrutledge.blogspot.com/p/ubuntu-mobile-project.html

Sam Horne’s mock-ups are ‘Unity-esque’ in appearance, with a strong emphasis given to familiar Unity elements such as the launcher and a ‘Dash’ style search – both of which are played to great use on his home-screen design in particular.

“I would like to share with you my own Ubuntu for mobile phones mock-up, just as a way of giving my two cents.” Sam told us when mailing in his designs.

“I gave the design some real thought (even if it’s not a perfect mock-up) to truly show how I’d make the mobile OS. Getting the design right is a big deal in my head, so I used my own HTC Desire to look at  them every time I made a change, just to make sure it felt natural or doable on an actual smartphone.”

ginjaninja405.deviantart.com/art/Ubuntu-with-mobile-Unity-268863703

Serial designer Musl1m took to his DeviantArt with a well thought out, and very Faenza styled, design.

I love the approach to app navigation, particularly in his ‘Ubuntu Software Centre’ design.

musl1m.deviantart.com/art/Ubuntu-Phone-Explained-271229476

 

posted @ 2011-12-06 19:47 RTY 阅读(323) | 评论 (0)编辑 收藏

DeVeDe 是一个用来创建可在家庭DVD播放器中放映的DVD视频光盘,支持几乎所有的视频格式,采用 Python 开发。

新版本增加对 ffmpeg 的支持,修复了菜单显示的问题,支持任务完成后自动关机的功能。

posted @ 2011-11-22 20:28 RTY 阅读(220) | 评论 (0)编辑 收藏

The reference to the wiki syntax for Google Code projects
Restrict-AddWikiComment-Commit
Updated Aug 17, 2011 by dbentley@google.com

Wiki Syntax

Introduction

Each wiki page is stored in a .wiki file under the /wiki directory in a project's repository. Each file's name is the same as the wiki page name. And, each wiki file consists of optional pragma lines followed by the content of the page.

Pragmas

Optional pragma lines provide metadata about the page and how it should be displayed. These lines are only processed if they appear at the top of the file. Each pragma line begins with a pound-sign (#) and the pragma name, followed by a value.

Pragma Value
#summary One-line summary of the page
#labels Comma-separated list of labels (filled in automatically via the web UI)
#sidebar See Side navigation

Wiki-style markup

Paragraphs

Use one or more blank lines to separate paragraphs.

Typeface

Name/Sample Markup
italic _italic_
bold *bold*
code `code`
code {{{code}}}
superscript ^super^script
subscript ,,sub,,script
strikeout ~~strikeout~~

You can mix these typefaces in some ways:

Markup Result
_*bold* in italics_ bold in italics
*_italics_ in bold* italics in bold
*~~strike~~ works too* strike works too
~~as well as _this_ way round~~ as well as this way round

Code

If you have a multiline code block that you want to display verbatim, use the multiline code delimiter:

{{{
def fib(n):
  if n == 0 or n == 1:
    return n
  else:
    # This recursion is not good for large numbers.
    return fib(n-1) + fib(n-2)
}}}

Which results in:

def fib(n):
  if n == 0 or n == 1:
    return n
  else:
    # This recursion is not good for large numbers.
    return fib(n-1) + fib(n-2)

For more control over the syntax higlighting, the <code> tag allows you to specify a file extension:

<code language="xml">
<hello target="world"/>
</code>

To disable highlighting entirely, use the <pre> tag.

Headings

= Heading =
== Subheading ==
=== Level 3 ===
==== Level 4 ====
===== Level 5 =====
====== Level 6 ======

Dividers

Four or more dashes on a line by themselves results in a horizontal rule.

Lists

Google Code wikis support both bulleted and numbered lists. A list must be indented at least one space to be recognized as such. You can also nest lists one within the other by appropriate use of indenting:

The following is:
  * A list
  * Of bulleted items
    # This is a numbered sublist
    # Which is done by indenting further
  * And back to the main bulleted list

 * This is also a list
 * With a single leading space
 * Notice that it is rendered
  # At the same levels
  # As the above lists.
 * Despite the different indentation levels.

The following is:

  • A list
  • Of bulleted items
    1. This is a numbered sublist
    2. Which is done by indenting further
  • And back to the main bulleted list
  • This is also a list
  • With a single leading space
  • Notice that it is rendered
    1. At the same levels
    2. As the above lists.
  • Despite the different indentation levels.

Quoting

Block quotes place emphasis on a particular extract of text in your page. Block quotes are created by indenting a paragraph by at least one space:

Someone once said:

  This sentence will be quoted in the future as the canonical example
  of a quote that is so important that it should be visually separate
  from the rest of the text in which it appears.

Someone once said:

This sentence will be quoted in the future as the canonical example of a quote that is so important that it should be visually separate from the rest of the text in which it appears.

Links

Links are central to the wiki principle, as they create the web of content. Google Code wiki permits both internal (within the wiki) and external links, and in some cases automatically creates a link when it recognizes either a WikiWord or an URL.

Internal wiki links

Internal links within a wiki are created using the syntax below. If you add a wiki link to a page that does not exist, the link will appear with a LittleLink? to project committers and owners. Clicking that link will take you to the page creation form where you can enter content for that page.

If you are not logged in, wiki links that point to non-existent pages will appear as plain text, without the link to the page creation form. When you create the target page, the link will become a normal hyperlink for all viewers of the page.

WikiSyntax is identified and linked automatically

Wikipage is not identified, so if you have a page named [Wikipage] you
need to link it explicitly.

If the WikiSyntax page is actually about reindeers, you can provide a
description, so that people know you are actually linking to a page on
[WikiSyntax reindeer flotillas].

If you want to mention !WikiSyntax without it being autolinked, use an
exclamation mark to prevent linking.

WikiSyntax is identified and linked automatically

Wikipage is not identified, so if you have a page named Wikipage you need to link it explicitly.

If the WikiSyntax page is actually about reindeers, you can provide a description, so that people know you are actually linking to a page on reindeer flotillas.

If you want to mention WikiSyntax without it being autolinked, use an exclamation mark to prevent linking.

Links to anchors within a page

Each heading defines a HTML anchor with the same name as the heading, but with spaces replaced by underscores. You can create a link to a specific heading on a page like this:

[WikiSyntax#Wiki-style_markup]

And it will render as: WikiSyntax#Wiki-style_markup.

Links to issues and revisions

You can easily link to issues and revisions using the following syntax.

  • issue 123 will be autolinked to issue number 123 in the current project. You can include a # or not. If the issue has been closed, the link will appear as a cross-out rather than an underline. Hovering your mouse over such a link shows the issue summary.
  • issue PROJECTNAME:122 will be autolinked to that issue number in the specified project. This is useful when your project depends on work being done in related projects.
  • r123 will be autolinked to the revision detail page for that revision in the current project.

There is currently no way to disable this type of autolinking. See issue 996.

For example: Please add a comment on issue 123 rather than adding more review comments to r456. 

Renders as: Please add a comment on  issue 123  rather than adding more review comments to r456.

Links to external pages

You can of course link to external pages from your own wiki pages, using a syntax similar to that for internal links:

Plain URLs such as http://www.google.com/ or ftp://ftp.kernel.org/ are
automatically made into links.

You can also provide some descriptive text. For example, the following
link points to the [http://www.google.com Google home page].

If your link points to an image, it will get inserted as an image tag
into the page:

http://code.google.com/images/code_sm.png

You can also make the image into a link, by setting the image URL as
the description of the URL you want to link:

[http://code.google.com/ http://code.google.com/images/code_sm.png]

Plain URLs such as http://www.google.com/ or ftp://ftp.kernel.org/ are automatically made into links.

You can also provide some descriptive text. For example, the following link points to the Google home page.

You can also make the image into a link, by setting the image URL as the description of the URL you want to link:

[http://code.google.com/ http://code.google.com/images/code_sm.png]

Links to images

If your link points to an image (that is, if it ends in .png, .gif, .jpg or .jpeg), it will get inserted as an image into the page:

http://code.google.com/images/code_sm.png

If the image is produced by a server-side script, you may need to add a nonsense query string parameter to the end so that the URL ends with a supported image filename extension.

http://chart.apis.google.com/chart?chs=200x125&chd=t:48.14,33.79,19.77|83.18,18.73,12.04&cht=bvg&nonsense=something_that_ends_with.png

Tables

Tables are created by entering the content of each cell separated by || delimiters. You can insert other inline wiki syntax in table cells, including typeface formatting and links.

|| *Year* || *Temperature (low)* || *Temperature (high)* ||
|| 1900 || -10 || 25 ||
|| 1910 || -15 || 30 ||
|| 1920 || -10 || 32 ||
|| 1930 || _N/A_ || _N/A_ ||
|| 1940 || -2 || 40 ||
Year Temperature (low) Temperature (high)
1900 -10 25
1910 -15 30
1920 -10 32
1930 N/A N/A
1940 -2 40

HTML support

To aid in the presentation of a wiki page there is some support for HTML. HTML tags are only supported in wiki pages, not in page comments.

HTML syntax can be used in conjunction with wiki syntax, but it is recommended against doing so where possible.

The following HTML tags and attributes are currently supported:

HTML TagSupported Attributes
atitle dir lang href
btitle dir lang
brtitle dir lang
blockquotetitle dir lang
codetitle dir lang language [1]
ddtitle dir lang
divtitle dir lang
dltitle dir lang
dttitle dir lang
emtitle dir lang
fonttitle dir lang face size color
h1title dir lang
h2title dir lang
h3title dir lang
h4title dir lang
h5title dir lang
ititle dir lang
imgtitle dir lang src alt border height width align
lititle dir lang
oltitle dir lang type start
ptitle dir lang align
pretitle dir lang
qtitle dir lang
stitle dir lang
spantitle dir lang
striketitle dir lang
strongtitle dir lang
subtitle dir lang
suptitle dir lang
tabletitle dir lang align valign cellspacing cellpadding border width height
tbodytitle dir lang align valign cellspacing cellpadding border width height
tdtitle dir lang align valign cellspacing cellpadding border width height
tfoottitle dir lang align valign cellspacing cellpadding border width height
thtitle dir lang align valign cellspacing cellpadding border width height
theadtitle dir lang align valign cellspacing cellpadding border width height colspan rowspan
trtitle dir lang align valign cellspacing cellpadding border width height colspan rowspan
tttitle dir lang
utitle dir lang
ultitle dir lang type
vartitle dir lang

[1] The language attribute of the code tag is the file extension of the language used in the code block. It is used as a hint for the syntax highlighter.

Escaping HTML Tags

When you want to display html tags directly on your wiki page (as opposed to rendered), you will need to escape each HTML tag.

HTML tags can be escaped as shown in the table below:

MarkupResult
`<hr>`<hr>
{{{<hr>}}}<hr>



Comments

The wiki supports embedded comments to help explain the contents of a wiki page. Anything inside the comment block is removed from the rendered page, but is visible when editing or viewing the source for that page.

<wiki:comment>
This text will be removed from the rendered page.
</wiki:comment>

+1 Button

Use <g:plusone></g:plusone> to add a +1 button to the page. Example:

<g:plusone size="medium"></g:plusone>

The count, size, and href parameters are supported; see http://code.google.com/apis/+1button/ for documentation.

Gadgets

You can embed Gadgets on your wiki pages with the following syntax:

<wiki:gadget url="http://example.com/gadget.xml" height="200" border="0" /> 

Valid attributes are:

  • url: the URL of the gadget
  • width: the width of the gadget
  • height: the height of the gadget
  • title: a title to display above the gadget
  • border: "0" or "1", whether to draw a border around the gadget
  • up_*: Gadget user preference parameters
  • caja: "0" or "1", whether to use Caja to render the gadget. Caja helps protect users from malicious or accidental errors in third party gadgets.

WorkingWithGoogleGadgets describes how to create gadgets for Google Code. It also provides a few helpful suggestions that can make it easier to publish gadgets and to integrate with other Google products such as iGoogle.

InterestingDeveloperGadgets shows some sample gadgets you may want to include on your project pages.

Videos

You can embed videos with the following syntax:

<wiki:video url="http://www.youtube.com/watch?v=3LkNlTNHZzE"/>

Valid attributes are:

  • url: the URL of the video
  • width: the width of the embedded video
  • height: the height of the embedded video

Right now we support videos from Youtube and Google Video, but other containers can be added to our open source gadgets project.

Navigation

Table of Contents

An inline table of contents can be generated from page headers on a wiki page. Add the following syntax to a page in the location the table of contents should be displayed:

<wiki:toc max_depth="1" />

Valid attributes are:

  • max_depth: the maximum header depth to display in the table of contents

Side navigation

You can specify the sidebar for a wiki page by selecting another wiki page that defines your side navigation. The doctype project uses the sidebar extensively across its wiki.

One way of adding a sidebar is by setting the #sidebar pragma, as shown below. Alternatively, the sidebar pragma can be left blank if no side navigation is desired.

#sidebar TableOfContents

The side navigation that is defined should be in the format of a simple list, as shown below.

 * [Articles HOWTO articles]
    * [ArticlesXSS Web security]
    * [ArticlesDom DOM manipulation]
    * [ArticlesStyle CSS and style]
    * [ArticlesTips Tips and tricks]
  * [DOMReference DOM reference]
  * [HTMLElements HTML reference]
  * [CSSReference CSS reference]

A default sidebar page can also be specified for all wiki pages by project owners through the Wiki settings in the Administer tab. If a #sidebar pragma is also specified, it will take precedence on the page.

Localizing Wiki Content

Along with the default language for the wiki, which can be set through the Wiki settings in the Administer tab, additional languages are also supported. If more than one language is available, based on a user's language preference (e.g. browser language), the wiki will try to serve the page for the appropriate language. If no wiki page exists for that language, it will fall back to the default language. Comments, however, are shared amongst all translations of a wiki page.

New translations for a page cannot be added through the web interface and have to be added through the Subversion repository.

To add a translation of a page, first checkout the wiki from Subversion:

svn checkout https://yourproject.googlecode.com/svn/wiki/ yourdirectory -username yourusername

Then create a new directory under /wiki/ using the two letter ISO-639 code as the name of that directory. Place all translated files in the new directory and submit those changes to the Subversion repository.

The following is an example of a valid wiki directory:

wiki/
   ja/
      ProjectHistory.wiki
      StyleGuide.wiki
   zh-Hans/
      ProjectHistory.wiki
      StyleGuide.wiki
   zh-Hant/
      ProjectHistory.wiki
      StyleGuide.wiki
   ProjectHistory.wiki
   StyleGuide.wiki

Once the files been be submitted to the project's subversion repository, they can now be edited through the wiki's web editor. The process is the same for Mercurial or Git projects, except that the wiki lives in the root directory (not wiki/) of https://wiki.yourproject.googlecode.com/hg/ or https://wiki.yourproject.googlecode.com/git/.

Note: The wiki accepts a subset of ISO-639 two letter language codes, where a few of the codes (such as zh_Hans) contain locale-specific codes. Such locale-specific codes should use a hyphen (zh-Hans) separator. A few example language codes have been specified in the table below.

Language (Locale) Directory Name
Arabic ar
Bulgarian bg
Chinese (China) zh-Hans
Chinese (Taiwan) zh-Hant
Croatian hr
Czech cs
Danish da
Dutch nl
English (United Kingdom) en-GB
English (United States) en-US
Finnish fi
French fr
German de
Greek el
Hebrew he
Hungarian hu
Italian it
Japanese ja
Korean ko
Norwegian no
Polish pl
Portuguese (Brazil) pt-BR
Romanian ro
Russian ru
Slovak sk
Spanish es
Swedish sv
Turkish tr

The content on this page created by Google is licensed under the Creative Commons Attribution 3.0 License. User-generated content is not included in this license.

posted @ 2011-11-09 06:52 RTY 阅读(523) | 评论 (0)编辑 收藏

Google Breakpad 完全解析(二) —— Windows前台实现篇

2011年02月7日 — Asp J

Table of contents for Google Breakpad 完全解析

  1. Google Breakpad 完全解析(一) —— Windows入门篇
  2. Google Breakpad 完全解析(二) —— Windows前台实现篇

原创文章,转载请标明出处:Soul Apogee (http://bigasp.com),谢谢。

好,看完了如何使用breakpad,我们现在看看breakpad在Windows下到底是如何实现的呢?

代码结构

在我们来看breakpad是如何实现其强大的功能之前,我们先来看一下他的代码结构吧。

Google breakpad的源代码都在src的目录下,他分为如下几个文件夹:
client:这下面包含了前台应用程序中捕捉dump的部分代码,里面按照平台分成各个子文件夹
common:前台后台都会用到的部分基础代码,字符串转换,内存读写,md5神马的
google_breakpad:breakpad中公共的头文件
processor:用于在后台处理崩溃的核心代码
testing:测试工程
third_party:第三方库
tools:一些小工具,用于处理dump文件和符号表

我们先来看Windows下前台实现的部分,也就是client文件夹下的代码。

breakpad的崩溃捕获机制

在Windows下捕获崩溃,大家很容易会想到那个捕获结构化异常的Api:SetUnhandledExceptionFilter

breakpad中也使用了这个Api来实现的崩溃捕获,另外,breakpad还捕获了另外两种C++运行库提供的崩溃,一种是使用_set_purecall_handler捕获纯虚函数调用产生的崩溃,还有一种是使用_set_invalid_parameter_handler捕获错误的参数调用产生的崩溃。

1
2
3
4
5
6
7
8
9
10
    if (handler_types & HANDLER_EXCEPTION)
      previous_filter_ = SetUnhandledExceptionFilter(HandleException);
 
#if _MSC_VER >= 1400  // MSVC 2005/8
    if (handler_types & HANDLER_INVALID_PARAMETER)
      previous_iph_ = _set_invalid_parameter_handler(HandleInvalidParameter);
#endif  // _MSC_VER >= 1400
 
    if (handler_types & HANDLER_PURECALL)
      previous_pch_ = _set_purecall_handler(HandlePureVirtualCall);

另外由于C++运行库提供的崩溃回调中,并不会提供当前的线程现场和崩溃信息,所以breakpad会自己生成好这些信息,然后请求生成dump。
这里值得一说的是,在非异常崩溃处理中,breakpad获取线程现场使用的函数是RtlCaptureContext而不是GetThreadContext。
RtlCaptureContext只能捕获当前线程的现场,而GetThreadContext可以捕获任意线程的现场,只要有这个线程的句柄即可。
但是GetThreadContext有两个不好的地方:不能获取当前线程的现场;获取现场前必须先用SuspendThread暂停目标线程。
而RtlCaptureContext虽然只能获取当前线程的现场,但是调用他时可以不用暂停线程的运行。
对于breakpad来说,崩溃发生后越早获取现场就越好,所以breakpad使用RtlCaptureContext函数作为他的线程获取函数。

breakpad中的C/S结构

由于breakpad是在进程外抓取dump,所以breakpad需要实现一个C/S结构来处理崩溃进程抓取dump的请求。

1. breakpad跨进程通信的实现
breakpad中使用了命名管道来实现IPC。

在客户端,初始化ExceptionHandler的时候,如果指定了PipeName,也就表示此时需要使用进程外的dump抓 取,ExceptionHandler,会建立一个 CrashGenerationClient的对象,由这个对象连接服务端,将自己注册到服务端上 去。
大家可以参看exception_handler.cc中的ExceptionHandler::Initialize函数。

在服务端,初始化CrashGenerationServer的时候,就会建立一个命名管道,并等待客户端来连接。一旦有客户端连接上来,服务端会 为每一个客户端生成一个ClientInfo的对象,之后用这个对象来管理所有的客户端,一旦有崩溃发生,服务端都会从这个对象中取出dump所需要的信 息。
大家可以参看crash_generation_server.cc中的CrashGenerationServer::HandleReadDoneState函数。

2. breakpad捕获崩溃生成dump的流程
breakpad进程外生成dump的流程大概如下:
google-breakpad-out-of-process-dump:
google-breakpad-out-of-process-dump
这段流程的代码就是crash_generation_client.cc和crash_generation_server.cc。

有两个简单的问题,这里说明一下,高手们就请直接忽略吧,咩哈哈:
在服务端如何为客户端生成事件句柄?
使用DuplicateHandle,即可把任意一个内核对象的句柄复制到其他进程,并且可以指定产生的句柄的权限。

如何异步的等待一个事件?
使用RegisterWaitForSingleObject,即可异步的等待一个事件,当事件发生的时候,就可以回调到一个指定的回 调函数中,但是要注意的是,RegisterWaitForSingleObject会在一个新的线程中来等待这个事件,此处很容易产生多线程的调用,需 要注意线程问题。

3. 服务端关键数据结构:ClientInfo
ClientInfo是服务端中最重要的数据结构,服务端通过它来管理所有的客户端。客户端注册时,会保存或生成里面所有的信息,在客户端请求生成dump的时候,服务端就会通过ClientInfo获取所有客户端的信息。ClientInfo中保存了如下信息:

  • 客户端进程pid和句柄
  • 生成Minidump的类型
  • 自定义的客户端信息
  • 客户端崩溃的线程ID
  • 客户端崩溃的信息
  • 客户端请求崩溃所使用的事件句柄

这里有一个问题:在客户端发生崩溃时,服务器如何通过ClientInfo获取到客户端的崩溃信息呢?

客户端中有几个用于保存崩溃信息的变量,在注册时,客户端会将这几个变量的地址发送至服务端,服务端将其保存在ClientInfo中,然后当崩溃 发生的时候,服务端就可以通过ReadProcessMemory读取客户端中的信息,从而生成dump。这样做就避免了每次发生崩溃,都要通过Pipe 将崩溃信息传递到服务端中去了。

这些变量分别是:崩溃的线程ID,EXCEPTION_POINTERS和MDRawAssertionInfo。
EXCEPTION_POINTERS和MDRawAssertionInfo的区别在于,异常崩溃的信息会被写入EXCEPTION_POINTERS,非异常崩溃(非法参数和纯虚函数调用)的信息会被写入MDRawAssertionInfo中。

dump文件的上传

在breakpad的工程中,有一个工程叫做:crash_report_sender,里面是一个上传崩溃文件的类,他的实现很简单,他使用Windows Internet Api来完成dump文件的上传。
在使用crash_report_sender时,可以为其指定一个checkpoint_file。

1
explicit CrashReportSender(const wstring &checkpoint_file);

这个文件只有一个作用,就是用来保存上次上传崩溃的时间和今天上传过的崩溃的次数。通过这个文件,我们就可以来设置每日上传的崩溃的最大数量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
CrashReportSender::CrashReportSender(const wstring &checkpoint_file)
    : checkpoint_file_(checkpoint_file),
      max_reports_per_day_(-1),
      last_sent_date_(-1),
      reports_sent_(0) {
  FILE *fd;
  if (OpenCheckpointFile(L"r", &fd) == 0) {
    ReadCheckpoint(fd);
    fclose(fd);
  }
}
 
ReportResult CrashReportSender::SendCrashReport(
    const wstring &url, const map<wstring, wstring> &parameters,
    const wstring &dump_file_name, wstring *report_code) {
  int today = GetCurrentDate();
  if (today == last_sent_date_ &&
      max_reports_per_day_ != -1 &&
      reports_sent_ >= max_reports_per_day_) {
    return RESULT_THROTTLED;
  }
 
  // 上传文件部分代码,省略
}

调整每日上传崩溃的最大数量的函数是set_max_reports_per_day。

需要注意的是:在上传dump文件的时候,crash_report_sender并不会对dump文件进行分析,而是直接上传整个dump文件, 如果你需要上传的dump文件非常大的话,可以考虑把崩溃分析处理的逻辑放入前台,通过去重或者直接上传分析结果,减少上传的文件大小。

breakpad存在的问题

进程外生成dump有很多好处,其中最大的好处就是不会被崩溃进程影响,这样dump的过程就不容易出错,但是这样也有一定的弊端。

1. 部分崩溃无法抓取
在一些极端的崩溃,如堆栈溢出之类的崩溃,进程外抓取dump有时候会失败。

2. 无法抓取死锁或者其他原因导致的进程僵死
breakpad现在没有检测进程死锁的代码,也没有在服务端控制客户端请求dump的代码,所以现在breakpad无法抓取死锁等进程僵死的问题。不过因为breakpad的定位是处理崩溃,如果有这种需要的童鞋,可以自行修改breakpad的代码,添加这些功能。

3. 对服务端有依赖
如果指定了在使用进程外抓取dump,breakpad对服务端就有依赖。主要体现在抓取dump时,如果服务端不存在,客户端将无法正常抓取dump,甚至有时会出现阻塞。

当然对于这些问题,随着breakpad的发展肯定会越来越完善。如果,你遇到了了这些问题,而又绕过不了,那就改代码,并且提交给breakpad吧,开源项目就是这么发展的。

好,到此breakpad的Windows实现就已经说完了,如果有神马问题,还请多多指教。谢谢大家。

北京德胜门中医院http://www.0531jsk.com/德胜门中医院

posted @ 2011-11-07 21:36 RTY 阅读(2342) | 评论 (0)编辑 收藏

Google Breakpad 完全解析(一) —— Windows入门篇

2011年01月23日 — Asp J

Table of contents for Google Breakpad 完全解析

  1. Google Breakpad 完全解析(一) —— Windows入门篇
  2. Google Breakpad 完全解析(二) —— Windows前台实现篇

原创文章,转载请标明出处:Soul Apogee (http://bigasp.com),谢谢。

Google breakpad是 一个非常实用的跨平台的崩溃转储和分析模块,他支持Windows,Linux和Mac和Solaris。由于他本身跨平台,所以很大的减少我们在平台移 植时的工作,毕竟崩溃转储,每个平台下都不同,使用起来很难统一,而Google breakpad就帮我们做到了这一点,不管是哪个平台下的崩溃,都能够进行统一的分析。现在很多工程都在使用他:最著名的几个如 Chrome,Firefox,Picasa和Google Earth。另外他的License是BSD的,也就是说,我们即便是在商业软件中使用,也是合法的,哈哈,这么好的东西,我们能放过么?现在就让我们来 看看这个神奇的软件吧。

原理简介

breakpad抓取dump的方式和一般我们抓取dump的方式不一样。在breakpad的wiki上有一幅图可以很好的概括他的原理。

breakpad把应用程序分成三个部分,代码,breakpad客户端和调试信息。

1. 在build system中,通过symbol dumper用平台相关的调试信息生成平台无关的symbol文件。这样做的好处很明显,一旦平台无关了,所有平台的崩溃就可以做统一的分析了。
2. breakpad采取进程外转储和分析崩溃的方式,他使用C/S结构,客户端用来捕获当前进程中发生的崩溃,并通知服务端崩溃发生。服务端用来响应客户端,抓取dump文件。这样做的目的是为了减少崩溃进程对dump的影响。
3. Dump生成后转发到崩溃分析器中,这个部分可以在本地也可以在服务器上,他对Dump文件进行解析,生成可读的堆栈信息。

这就是breakpad处理dump大概的流程。

对于原理的介绍google写的已经相当好了。更多的详细信息,可以直接移步到breakpad的wiki

安装和编译

breakpad的编译比较曲折,所以在此记录一下。

编译breakpad,请确认你的机器上装有以下的软件:
1. python 2.4.3
请不要使用python3,会报错。另外python2中推荐这个版本,使用新的版本在编译其他google的工程时有时会报错

2. Windows SDK 7
如果没有这个,编译会报错。另外这个是在线安装,时间很久,最好并行做其他的事情。

3. VS2005的补丁
KB918559
KB926601
KB935225
KB943969
KB947315

已经安装了以上软件的童鞋,就可以开始进行下面的工作鸟

1. 使用svn把代码checkout下来

1
2
# Non-members may check out a read-only working copy anonymously over HTTP.
svn checkout http://google-breakpad.googlecode.com/svn/trunk/ google-breakpad-read-only

2. 设置Windows SDK 7
装过其他版本Windows SDK的童鞋,记得一定要进行这一步,SDK的安装程序,并不会帮你设置VS。
运行开始菜单->程序->Microsoft Windows SDK v7.0->Visual Studio Registration->Windows SDK Configuration Tool,选择v7.0,点击Make Current。

3. 为python设置环境变量
由于breakpad使用python来生成Windows下的工程文件,所以需要将python所在目录,设置到环境变量PATH中去。

4. 生成Windows工程文件

1
2
3
4
cd "源码目录/src/tools/gyp"
 
# 注意,此处不能使用全路径,不然会出错
gyp.bat "../../client/windows/breakpad_client.gyp"

此时,在src/client/windows下就可以看到生成好的breakpad_client.sln了。运行吧!

5. Hello World!
编译build all,现在一般是不会报错了,如果报错,请检查是不是漏了什么步骤,特别是补丁。
编译完成之后,运行crash_generation_app吧,这是他的测试程序,dump的默认位置保存在C:Dumps下,请注意先建立好目录,不然会无法使用。
启动测试程序之后,此时还不能抓取dump,因为这个是breakpad中的服务器端,需要再启动一个测试程序,在第二个测试程序中,我们就可以试验Client菜单中的各种崩溃了。这些崩溃都会被抓住转存到C:Dumps目录下。

如何使用breakpad

在Windows下使用breakpad的方法很简单,只需要创建一个ExceptionHandler的类即可,大家可以在crash_generation_app这个工程中找到示例代码,也可以直接移步Wiki,上面说的也很详细。

1.进程内抓取Dump文件

进程内抓取Dump文件是最简单的breakpad的用法。使用方法很简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const std::wstring s_strCrashDir = L"c:\dumps";
 
bool
InitBreakpad()
{
    google_breakpad::ExceptionHandler *pCrashHandler =
        new google_breakpad::ExceptionHandler(s_strCrashDir,
        onExceptionFilter,
        onMinidumpDumped,
        NULL,
        google_breakpad::ExceptionHandler::HANDLER_ALL,
        MiniDumpNormal,
        NULL,
        NULL);
 
    if(pCrashHandler == NULL) {
        return false;
    }
 
    return true;
}

2.进程外抓取Dump文件

使用进程外抓取Dump时,需要指定服务端和客户端,在服务端中需要创建CrashGenerationServer的实例,而在客户端中则只需要创建ExceptionHandler即可。此外,如果服务端自己需要抓进程内的Dump,请将pipe的参数置为NULL。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
const wchar_t s_pPipeName[] = L"\\.\pipe\breakpad\crash_handler_server";
const std::wstring s_strCrashDir = L"c:\dumps";
 
bool
InitBreakpad()
{
    google_breakpad::CrashGenerationServer *pCrashServer =
        new google_breakpad::CrashGenerationServer(s_pPipeName,
        NULL,
        onClientConnected,
        NULL,
        onClientDumpRequest,
        NULL,
        onClientExited,
        NULL,
        true,
        &s_strCrashDir);
 
    if(pCrashServer == NULL) {
        return false;
    }
 
    // 如果已经服务端已经启动了,此处启动会失败
    if(!pCrashServer->Start()) {
        delete pCrashServer;
        pCrashServer = NULL;
    }
 
    google_breakpad::ExceptionHandler *pCrashHandler =
        new google_breakpad::ExceptionHandler(s_strCrashDir,
        onExceptionFilter,
        onMinidumpDumped,
        NULL,
        google_breakpad::ExceptionHandler::HANDLER_ALL,
        MiniDumpNormal,
        (pCrashServer == NULL) ? s_pPipeName : NULL, // 如果是服务端,则直接使用进程内dump
        NULL);
 
    if(pCrashHandler == NULL) {
        return false;
    }
 
    return true;
}

使用breakpad的时候,有两个地方需要注意:
1. 记得把breakpad的solution下的几个工程,包含到你开发的工程中,或者直接包含他们的lib。
common:基础功能,包含一个对GUID的封装和http上传的类。
exception_handler:用来捕获崩溃的类。
crash_generation_server:breakpad的服务端,用来在产生崩溃时抓取dump。
crash_generation_client:breakpad的客户端,用来捕获当前进程的崩溃。

2. 在初始化breakpad之前,记得先创建好dump文件的目录,不然breakpad服务端将不能正常的写dump,这会导致breakpad客户端在崩溃时无限等待服务端dump写完的消息,最后失去响应。

posted @ 2011-11-07 21:34 RTY 阅读(1762) | 评论 (0)编辑 收藏

调试Release发布版程序的Crash错误 (转)

调试Release发布版程序的Crash错误

http://blog.sina.com.cn/s/blog_48f93b530100fsln.html

 

Windows平台下用C++开发应用程序,最不想见到的情况恐怕就是程序崩溃,而要想解决引起问题的bug,最困难的应该就是调试release版本了。因为release版本来就少了很多调试信息,更何况一般都是发布出去由用户使用,crash的现场很难保留和重现。本文将给出几个解决方案,完成对release版应用程序crash错误的调试。(本文只讨论Windows平台MSVC环境下的调试,对于其他平台和开发环境没有关注,请大家自己借鉴和尝试。)

 

    方案一:崩溃地址 + MAP文件

    这种方案只能对VC7以前的版本开发的程序使用。 

    1、崩溃地址

     所谓崩溃地址就是引起程序崩溃的内存地址,在WinXP下应用程序crash的对话框如下图:

clip_image001

clip_image002

clip_image003

    上面第2张图中画红线的值为crash的代码偏移地址,第3张图为即crash绝对地址;一般引起crash的原因多为内存操作错误,我们用这两个地址和MAP文件就能定位出错的代码行。

    2MAP文件

    MAP文件是记录应用程序信息的文件(文本文件),里面大概包含了程序的全局符号、源码模块名、源码文件和行号等信息,而这些信息能够帮助我们定位出错的代码行。

    怎样生成MAP文件呢?以VC6为例,在 Project Settings -> C/C++ -> Debug info中,选择 Line Numbers Only ;在 Project Settings -> Link 中,选择 Generate mapfile项,并在Project Options 里面输入 /MAPINFO:LINES/MAPINFO:EXPORTS,重新编译程序就会生成.map文件。

    以上设置对应的编译链接选项分别分:

    /Zi — 表示生成pdb调试信息;

    /MAP[:filename] — 表示生成map文件名;

    /MAPINFO:EXPORTS — 表示生成的map文件中加入exported functions(生成DLL文件时);

    /MAPINFO:LINES — 表示生成的map文件中加入代码行信息。

    由于/MAPINFO:LINES选项在VC8以后的版本中不再支持,因此通过MAP文件中的信息和crash地址定位出错代码行就比较困难了,所以这种方案只能在VC7及以前的版本中使用。

    一个MAP文件片段示例如下: 

    clip_image004  

    clip_image005

    图中Rva+Base列的地址为该行函数对应的函数绝对地址,Address列中冒号后面的地址为函数相对偏移地址。   

    3、定位crash代码

    有了上面的介绍,定位crash代码就很简单了。用下面的公式来进行定位:

    崩溃行偏移 = 崩溃地址 - 崩溃函数绝对地址 + 函数相对偏移

    我们首先根据崩溃地址(绝对地址),按照找到第2张图中Rva+Base列的地址找到发生崩溃的函数(即崩溃地址大于该函数行的Rva+Base地址且小于下个函数的地址),然后找到该行对应的函数相对偏移地址,带入公式中,就得到了崩溃行偏移,该值表示崩溃行的代码相对于代码所在函数的偏移量。用该值去与第3张图中对应函数冒号后面的偏移量去比较,最接近的值前面的那个十进制数即为代码所在函数中的行号。

    ok,到此我们已经成功找到了崩溃的代码行,只不过这种方法还是比较费力,并且限制比较多,我们看看下面的方案。

上篇给出的方案一还要补充几句。通过“crash地址 + MAP文件”来定位出错代码位置虽然需要经过比较复杂的地址计算,但却是最简单实现的方式。如果仅仅想通过崩溃地址定位出错的函数,就更加方便了。我在网上找到一个解析MAP文件的小工具,可以非常清晰的列出每个函数的地址,并且可以将分析表格导出为Excel文件。工具下载地址:http://e.ys168.com/?tinyfun,工具目录下VCMapper.exe。

    另外上篇主要参考两篇文章:

    http://www.vckbase.com/document/viewdoc/?id=908

    http://www.vckbase.com/document/viewdoc/?id=1473

 

    方案二:崩溃地址 + MAP文件 + COD文件

    由于VC8以后的版本都不再支持MAP文件中产生代码行信息,因此我们寻找另一种定位方式:COD文件。

    1COD文件

    COD文件是一个包含了汇编码、二进制机器码和源代码对应信息的文件,每一个cpp都对应一个COD文件。通过这个文件,我们可以非常方便地进行定位。

   VC6中生成COD文件的设置方式为:Project Settings -> C/C++,在 Category 中选 Listing Files,在 Listing file type 组合框中选 Assembly,Machine code,and source。在VC8中生成COD文件的设置方式为:Project Properties -> C/C++ -> Output Files -> Assembler Output 项,选择 Assembly,Machine code,and Source(/Facs)。

   

    2、定位崩溃行

    下面通过举例进行说明。现在我有一个基于对话框的MFC应用程序CrashTest,在CCrashTestDlg::OnInitDialog函数中写入导致crash的代码语句(第99行),源文件如下:

    clip_image006

    根据崩溃地址(0x004012A3)以及MAP文件(定位片段图片如下),定位crash函数为OnInitDialog;并且我们可以很容易地计算出崩溃地址相对于崩溃函数的偏移量为 0x004012A3 - 0x004011E0 = 0xC3。

    clip_image007

    再来看看CrashTestDlg.cod文件,我们根据文件中源码信息找到OnInitDialog函数信息片段:

    clip_image008

    可以看到图片中第一行为OnInitDialog函数汇编代码的起始行;找到“int * p = NULL;”这一句源码,其前面的98表示这行代码在源文件中的行号,下面的000c1表示相对于函数开始位置的偏移量,后面的“33 c0”为机器码,“xor eax,eax”为汇编码。那么我们根据前面算出来的偏移量0xC3,找到对应出错的语句为99行:“*p = 5;”。

    总结一下定位步骤:

    1) 根据公式 崩溃语句在函数中偏移地址 = 崩溃地址 - 崩溃函数地址 计算出偏移量X;

    2) 根据公式 崩溃语句在COD文件中地址 = 崩溃函数在COD文件中地址 + X 计算出地址Y。其中崩溃函数在COD文件中地址为COD文件中函数起始括号“{”后面表明的地址,一般情况下为0x0000;

    3) 根据Y在COD文件中找到对应代码行。

   

    ok,方案二介绍完了。这种方法最大的好处是没有VC开发环境版本限制,而且COD文件里面包含的信息更加丰富,不但可以帮助我们定位crash,还能帮我们分析很多东西。当然,这也导致编译生成了很多信息文件。

根据前面两篇博文,我们要定位崩溃行代码,必须要自己根据相关信息文件进行计算。如果需要处理的量比较大,恐怕会很费力气。有没有更简单快速的办法呢?

    最直接的想法就是写一个小工具,根据规则和信息进行自动定位,不过开发起来也是要费一番功夫的。令人开心的是,我们可以找到类似的工具,而且是开源免费的!程序员的世界也许很多时候都是这么单纯而乐于分享!

   

    方案三:崩溃地址 + PDB文件 + CrashFinder

    CrashFinder是一个开源工具,作者是John Robbin,大家可以去他的blog上去找关于CrashFinder的信息。我们这里以CrashFinder2.5版本为例介绍,相关文章链接为:http://www.wintellect.com/CS/blogs/jrobbins/archive/2006/04/19/crashfinder-returns.aspx

    1PDB文件

    PDB(Program Database)文件中包含了exe程序所有的调试相关信息,具体可以查阅MSDN。当编译选项设置为/Zi,链接选项设置为/DEBUG,/OPT:REF时,就会生成工程的.pdb文件。具体到VC2005中,就是 Project Propertise -> C/C++ -> General -> Debug Information Format 项设置为 Program Database(/Zi),Linker -> Debugging -> Generate Debug Info 项设置为 Yes(/Debug),Linker -> Optimization -> References 项设置为 Eliminate Unreferenced Data(/OPT:REF)。

    只要设置以上选项,release版本也能生成PDB文件。当然,对应的应用程序也会稍大。

    2CrashFinder

    CrashFinder能够运行需要两个条件:一是系统必须要有dbghelp.dll文件;二是PDB文件必须与exe文件在一个路径下。对于dbghelp.dll,一般在系统system32路径下都有,如果没有下载一个放到这个目录下就可以了。

    先看一下CrashFinder的界面。

   

clip_image009

    用起来也非常简单。首先选择File->New或点击工具栏新建按钮,选择要调试的exe文件打开,会发现exe及所依赖的dll文件信息都已经加载进来。在下半部分的编辑框中输入崩溃地址(16进制),点右边的“Find”按钮,就会在下面显示崩溃的源文件路径、名称以及崩溃所在行号了,如下图所示。

clip_image010

   CrashFinder进行crash定位真的非常方便。但是我在使用过程中发现了一个bug,每次启动程序后,直接新建的话加载进来的exe模块都显示叉,提示找不到debug symbols。但是用打开按钮随便打开一个文件失败后,再新建就能成功。猜测可能是直接新建,定位PDB文件时的路径不对引起的。有源码,但是懒的看了呵呵,大家感兴趣可以试一下。

    好了,方案三就介绍到这里,后面还有更加强大的方案 : )

前面几个方案都是直接定位crash的代码位置,但是在比较大型的程序中,只知道这个信息还是远远不够的,我们希望知道更多关于调用函数顺序及变量值等信息,也就是crash时调用堆栈信息。

 

    方案四:SetUnhandledExceptionFilter + StackWalker

    这个方案需要自己动手往工程里添加代码了。要实现上面的想法,需要做两件事情:1、需要在crash时有机会对程序堆栈进行处理;2、对堆栈信息进行收集。

    1SetUnhandleExceptionFilter函数

    Windows平台下的C++程序异常通常可分为两种:结构化异常(Structured Exception,可以理解为与操作系统相关的异常)和C++异常。对于结构化异常处理(SEH),可以找到很多资料,在此不细说。对于crash错误,一般由未被正常捕获的异常引起,Windows操作系统提供了一个API函数可以在程序crash之前有机会处理这些异常,就是SetUnhandleExceptionFilter函数。(C++也有一个类似函数set_terminate可以处理未被捕获的C++异常。)

    SetUnhandleExceptionFilter函数声明如下:

    LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter(
      __in          LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
    );

    其中 LPTOP_LEVEL_EXCEPTION_FILTER 定义如下:

    typedef LONG (WINAPI *PTOP_LEVEL_EXCEPTION_FILTER)(
        __in struct _EXCEPTION_POINTERS *ExceptionInfo
    );
    typedef PTOP_LEVEL_EXCEPTION_FILTER LPTOP_LEVEL_EXCEPTION_FILTER;

    简单来说,SetUnhandleExceptionFilter允许我们设置一个自己的函数作为全局SEH过滤函数,当程序crash前会调用我们的函数进行处理。我们可以利用的是 _EXCEPTION_POINTERS 结构类型的变量ExceptionInfo,它包含了对异常的描述以及发生异常的线程状态,过滤函数可以通过返回不同的值来让系统继续运行或退出应用程序。

    关于 SetUnhandleExceptionFilter 函数的具体用法和示例请参考MSDN。

 

    2StackWalker
    现在我们已经有机会可以在crash之前对程序状态信息进行处理了,只需要生成并保存堆栈信息就大功告成了。Windows的dbghelp.dll库提供了一个函数可以得到当前堆栈信息:StackWalk64(在Win2K以前版本中为StackWalk)。该函数声明如下:

    BOOL WINAPI StackWalk64(
      __in          DWORD MachineType,
      __in          HANDLE hProcess,
      __in          HANDLE hThread,
      __in_out      LPSTACKFRAME64 StackFrame,
      __in_out      PVOID ContextRecord,
      __in          PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
      __in          PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
      __in          PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
      __in          PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
    );
    该函数的具体用法可以参考MSDN。在这里推荐一个牛人写好的StackWalker,可以直接拿来用,开源的。StackWalker提供了一个基类,给出了几个简单的接口,可以方便地生成堆栈信息,并且支持一系列VC版本,非常好用。我们可以自己写一个子类,并重载虚函数OnOutput,就可以将堆栈信息输出为特定格式了。StackWalker的地址为:http://www.codeproject.com/KB/threads/StackWalker.aspx

    不过对于Release版本来说,StackWalk64函数获得的堆栈信息有可能不完整。如果异常是由MFC的模块抛出,那么获得的堆栈可能缺少前面调用模块信息。另外,StackWalk64需要最新的dbghelp.dll文件支持才能工作;要正确输出crash的函数名和行号,需要要pdb文件支持。以上不足有可能影响输出信息的完整性和效果,而对于发布在外的程序,要带上pdb文件几乎不可能,因此这个方案还是有缺憾的,比较适用于本地的release版本调试。

    下一篇我们将介绍一个更加完善的解决方案

当我们把自己的release版本程序发布出去以后,一般都是在用户的机器上运行。这种情况下,对于第四种方案,因为需要pdb文件才能够正确生成堆栈调用的函数行号及代码行号,因此方案四只适用于本地release版的调试,否则只能生成不完整的堆栈信息。对于前三种方案,其实只需要用户告知崩溃地址,然后在本地查找crash地址就可以了,但是定位crash的过程非常不方便,如果crash的情况比较多,前三种方案都不合适。而且,前三种方案均不能生成堆栈调用信息,对于debug的作用有限。

    下面我们就来看一个更加完善的解决方案。

 

    方案五:SetUnhandledExceptionFilter + Minidump

    SetUnhandleExceptionFilter函数我们已经介绍过了,本方案的思路还是要利用我们自己的异常处理函数,来生成minidump文件。

    1Minidump概念

    minidump(小存储器转储)可以理解为一个dump文件,里面记录了能够帮助调试crash的最小有用信息。实际上,如果你在 系统属性 -> 高级 -> 启动和故障恢复 -> 设置 -> 写入调试信息 中选择“小内存转储(64 KB)”的话,当系统意外停止时都会在C:\Windows\Minidump\路径下生成一个.dmp后缀的文件,这个文件就是minidump文件,只不过这个是内核态的minidump。

   我们要生成的是用户态的minidump,文件中包含了程序运行的模块信息、线程信息、堆栈调用信息等。而且为了符合其mini的特性,dump文件是压缩过的。

    2、生成minidump文件

    生成minidump文件的API函数是MiniDumpWriteDump,该函数需要dbghelp.lib支持,其原型如下:

    BOOL WINAPI MiniDumpWriteDump(
      __in          HANDLE hProcess,
      __in          DWORD ProcessId,
      __in          HANDLE hFile,
      __in          MINIDUMP_TYPE DumpType,
      __in          PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
      __in          PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
      __in          PMINIDUMP_CALLBACK_INFORMATION CallbackParam
    );

    在我们的异常处理函数中加入以下代码:

    HANDLE hFile = ::CreateFile( _T("E:\\dumpfile.dmp"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
     if( hFile != INVALID_HANDLE_VALUE)
     {
         MINIDUMP_EXCEPTION_INFORMATION einfo;
         einfo.ThreadId = ::GetCurrentThreadId();
         einfo.ExceptionPointers = pExInfo;
         einfo.ClientPointers = FALSE;

        ::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, MiniDumpNormal, &einfo, NULL, NULL);
        ::CloseHandle(hFile);
     }

    其中,pExInfo变量为异常处理函数PEXCEPTION_POINTERS类型的参数。具体请参考MSDN。

    3、调试minidump

    调试dump文件首先需要pdb文件,因此我们build程序时需要设置 Debug Infomation Format 为 “Program Database(/Zi)”。其次,我们还要确保所用的dump文件与源代码、exe、pdb文件版本是一致的,这要求我们必须维护好程序版本信息。

    调试minidump最方便的环境就是VS了,我们只要将.dmp、.exe、.pdb文件放在一个路径下,保证源代码文件的路径与编译时的路径一致就可以了,剩下的就是VS帮我们完成。双击.dmp文件或者在文件打开工程中选择“dump files”,加载dump文件,然后按F5运行就能直接恢复crash时的现场了,你可以定位crash的代码,可以查看调用堆栈,可以查看线程和模块信息...一切都跟你设置断点调试一样,太强大了!看个截图吧。

clip_image012

    需要注意的是,对于release版的程序来说,很多代码是经过编译器优化过的,因此定位的时候可能会有所偏差,大家可以考虑设置选项去掉代码优化。

    其他可以调试minidump的工具还有WinDbg等,大家可以查阅相关资料。

    本文主要参考了这篇文章:http://vicchina.51.net/research/other/seh/minidumps/intro.htm

    下一篇,我们将给出一个调试release发布程序的完美解决方案,适合用户量较大的应用发布程序的调试。

上一篇我们已经给出了方案,能够非常方便的通过dump文件对crash错误进行调试和定位;从整个流程上看还差最后一步,即怎样拿到crash时产生的dump文件。如果可以让用户把文件发送过来自然不错,但对于类似免费共享软件等在互联网上发布的程序呢?我们的用户是不确定的,而且用户量有可能非常大,即使我们能想办法联系到用户,总不能挨个去收集crash信息吧。

    我们需要一种方案,能够提供crash信息汇报功能。

    我们可以架设一台服务器专门进行信息收集,只要客户端在crash时正确汇报即可,但是相应的维护成本和开发难度也不可忽视。有没有更简单的方法呢?还记得我的博文“为程序添加自动发送Email功能吗?这就是简单有效的方法!

 

    方案六:minidump + email

    我们只需要在异常处理时,先生成minidump信息文件,再用email方式将文件发送到指定邮箱就行了。剩下的就是我们每天查看邮箱,提取dump文件进行调试了。

    1Email功能

    首先我们来看一下email发送都需要哪些相关信息。

    a、发送端邮箱帐户;

    b、接收端邮箱帐户;

    c、email标题,一般应有软件名称及版本信息;

    d、email正文,一般应有简单的crash信息提示,以区别不同原因造成的crash;

    e、email附件,当然就是我们的dump文件了,还可以加上软件生成的log文件等。

    当然,对于标题应该尽量多加一些信息区别引起crash的原因,比如将crash的地址信息加到标题中;因为当每天有成百上千的crash汇报上来,重复的crash占大多数,把时间都花在区分它们身上有点太浪费。由此看来,前面方案中提到的StackWalker还是有些用处的,我们可以用它来生成一些crash的文字描述信息,写到标题或正文中去。

    dump文件的大小是否适合作为邮件的附件呢?实际上minidump产生的文件一般在几K到几十K之间,作为email的附件没有任何问题。

    关于发送email相关技术细节,已经在“为程序添加自动发送Email功能文中介绍了,大家可以参考。其实,对接受邮箱中邮件的处理还是很费时费力的,大家可以考虑写一些脚本将处理流程自动化,提高效率。

    2google breakpad

    google breakpad是一个开源的跨平台crash report系统,光从开源和跨平台这两个特点上来看,它就足以称的上是一个完善而有效的工具了。其实,breakpad在整个crash report层次上给出了一个系统级的解决方案,也就是说它几乎能适应各种软件、各种平台的应用要求。

    breakpad的整体思路跟上面介绍的方案是相似的,只不过最后提交dump文件的方式更加完善。大家有兴趣可以去它的官方网址查阅相关资料:http://code.google.com/p/google-breakpad/

 

    ok,关于调试release发布程序的crash错误系列文章就写完了。这几篇文章给出的方案由简单到复杂,由简陋到完善,对crash调试有了一个比较全面的总结。当然,其中涉及到的概念和技术还很多,需要我们去不断学习和领悟,也希望大家能够互相交流。

posted @ 2011-11-07 21:15 RTY 阅读(1213) | 评论 (0)编辑 收藏

仅列出标题
共31页: First 3 4 5 6 7 8 9 10 11 Last