青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

posts - 319, comments - 22, trackbacks - 0, articles - 11
  C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

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

ExEinfo PE(可執(zhí)行程序檢查工具)V0.0.3.0 綠色版

exeinfo pe 是一款免費(fèi)的win32可執(zhí)行程序檢查器,它可以檢查程序的打包方式,exe保護(hù)等,可以幫助開發(fā)人員對(duì)程序進(jìn)行破解

posted @ 2012-02-01 20:24 RTY 閱讀(322) | 評(píng)論 (0)編輯 收藏

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

將鼠標(biāo)移動(dòng)到您要探測的窗體或控件上,即可獲取該窗體的句柄、類名、文本(包括密碼文本)、窗口位置、窗口尺寸、程式路徑、編程語言等信息。

posted @ 2012-02-01 20:22 RTY 閱讀(377) | 評(píng)論 (0)編輯 收藏

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

文件數(shù)字簽名克隆工具[單文件版]V1.00 綠色版

可復(fù)制其他文件數(shù)字簽名。

posted @ 2012-02-01 20:18 RTY 閱讀(357) | 評(píng)論 (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 閱讀(480) | 評(píng)論 (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 閱讀(354) | 評(píng)論 (0)編輯 收藏

DeVeDe 是一個(gè)用來創(chuàng)建可在家庭DVD播放器中放映的DVD視頻光盤,支持幾乎所有的視頻格式,采用 Python 開發(fā)。

新版本增加對(duì) ffmpeg 的支持,修復(fù)了菜單顯示的問題,支持任務(wù)完成后自動(dòng)關(guān)機(jī)的功能。

posted @ 2011-11-22 20:28 RTY 閱讀(252) | 評(píng)論 (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 閱讀(561) | 評(píng)論 (0)編輯 收藏

Google Breakpad 完全解析(二) —— Windows前臺(tái)實(shí)現(xiàn)篇

2011年02月7日 — Asp J

Table of contents for Google Breakpad 完全解析

  1. Google Breakpad 完全解析(一) —— Windows入門篇
  2. Google Breakpad 完全解析(二) —— Windows前臺(tái)實(shí)現(xiàn)篇

原創(chuàng)文章,轉(zhuǎn)載請(qǐng)標(biāo)明出處:Soul Apogee (http://bigasp.com),謝謝。

好,看完了如何使用breakpad,我們現(xiàn)在看看breakpad在Windows下到底是如何實(shí)現(xiàn)的呢?

代碼結(jié)構(gòu)

在我們來看breakpad是如何實(shí)現(xiàn)其強(qiáng)大的功能之前,我們先來看一下他的代碼結(jié)構(gòu)吧。

Google breakpad的源代碼都在src的目錄下,他分為如下幾個(gè)文件夾:
client:這下面包含了前臺(tái)應(yīng)用程序中捕捉dump的部分代碼,里面按照平臺(tái)分成各個(gè)子文件夾
common:前臺(tái)后臺(tái)都會(huì)用到的部分基礎(chǔ)代碼,字符串轉(zhuǎn)換,內(nèi)存讀寫,md5神馬的
google_breakpad:breakpad中公共的頭文件
processor:用于在后臺(tái)處理崩潰的核心代碼
testing:測試工程
third_party:第三方庫
tools:一些小工具,用于處理dump文件和符號(hào)表

我們先來看Windows下前臺(tái)實(shí)現(xiàn)的部分,也就是client文件夾下的代碼。

breakpad的崩潰捕獲機(jī)制

在Windows下捕獲崩潰,大家很容易會(huì)想到那個(gè)捕獲結(jié)構(gòu)化異常的Api:SetUnhandledExceptionFilter

breakpad中也使用了這個(gè)Api來實(shí)現(xiàn)的崩潰捕獲,另外,breakpad還捕獲了另外兩種C++運(yùn)行庫提供的崩潰,一種是使用_set_purecall_handler捕獲純虛函數(shù)調(diào)用產(chǎn)生的崩潰,還有一種是使用_set_invalid_parameter_handler捕獲錯(cuò)誤的參數(shù)調(diào)用產(chǎn)生的崩潰。

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++運(yùn)行庫提供的崩潰回調(diào)中,并不會(huì)提供當(dāng)前的線程現(xiàn)場和崩潰信息,所以breakpad會(huì)自己生成好這些信息,然后請(qǐng)求生成dump。
這里值得一說的是,在非異常崩潰處理中,breakpad獲取線程現(xiàn)場使用的函數(shù)是RtlCaptureContext而不是GetThreadContext。
RtlCaptureContext只能捕獲當(dāng)前線程的現(xiàn)場,而GetThreadContext可以捕獲任意線程的現(xiàn)場,只要有這個(gè)線程的句柄即可。
但是GetThreadContext有兩個(gè)不好的地方:不能獲取當(dāng)前線程的現(xiàn)場;獲取現(xiàn)場前必須先用SuspendThread暫停目標(biāo)線程。
而RtlCaptureContext雖然只能獲取當(dāng)前線程的現(xiàn)場,但是調(diào)用他時(shí)可以不用暫停線程的運(yùn)行。
對(duì)于breakpad來說,崩潰發(fā)生后越早獲取現(xiàn)場就越好,所以breakpad使用RtlCaptureContext函數(shù)作為他的線程獲取函數(shù)。

breakpad中的C/S結(jié)構(gòu)

由于breakpad是在進(jìn)程外抓取dump,所以breakpad需要實(shí)現(xiàn)一個(gè)C/S結(jié)構(gòu)來處理崩潰進(jìn)程抓取dump的請(qǐng)求。

1. breakpad跨進(jìn)程通信的實(shí)現(xiàn)
breakpad中使用了命名管道來實(shí)現(xiàn)IPC。

在客戶端,初始化ExceptionHandler的時(shí)候,如果指定了PipeName,也就表示此時(shí)需要使用進(jìn)程外的dump抓 取,ExceptionHandler,會(huì)建立一個(gè) CrashGenerationClient的對(duì)象,由這個(gè)對(duì)象連接服務(wù)端,將自己注冊(cè)到服務(wù)端上 去。
大家可以參看exception_handler.cc中的ExceptionHandler::Initialize函數(shù)。

在服務(wù)端,初始化CrashGenerationServer的時(shí)候,就會(huì)建立一個(gè)命名管道,并等待客戶端來連接。一旦有客戶端連接上來,服務(wù)端會(huì) 為每一個(gè)客戶端生成一個(gè)ClientInfo的對(duì)象,之后用這個(gè)對(duì)象來管理所有的客戶端,一旦有崩潰發(fā)生,服務(wù)端都會(huì)從這個(gè)對(duì)象中取出dump所需要的信 息。
大家可以參看crash_generation_server.cc中的CrashGenerationServer::HandleReadDoneState函數(shù)。

2. breakpad捕獲崩潰生成dump的流程
breakpad進(jìn)程外生成dump的流程大概如下:
google-breakpad-out-of-process-dump:
google-breakpad-out-of-process-dump
這段流程的代碼就是crash_generation_client.cc和crash_generation_server.cc。

有兩個(gè)簡單的問題,這里說明一下,高手們就請(qǐng)直接忽略吧,咩哈哈:
在服務(wù)端如何為客戶端生成事件句柄?
使用DuplicateHandle,即可把任意一個(gè)內(nèi)核對(duì)象的句柄復(fù)制到其他進(jìn)程,并且可以指定產(chǎn)生的句柄的權(quán)限。

如何異步的等待一個(gè)事件?
使用RegisterWaitForSingleObject,即可異步的等待一個(gè)事件,當(dāng)事件發(fā)生的時(shí)候,就可以回調(diào)到一個(gè)指定的回 調(diào)函數(shù)中,但是要注意的是,RegisterWaitForSingleObject會(huì)在一個(gè)新的線程中來等待這個(gè)事件,此處很容易產(chǎn)生多線程的調(diào)用,需 要注意線程問題。

3. 服務(wù)端關(guān)鍵數(shù)據(jù)結(jié)構(gòu):ClientInfo
ClientInfo是服務(wù)端中最重要的數(shù)據(jù)結(jié)構(gòu),服務(wù)端通過它來管理所有的客戶端。客戶端注冊(cè)時(shí),會(huì)保存或生成里面所有的信息,在客戶端請(qǐng)求生成dump的時(shí)候,服務(wù)端就會(huì)通過ClientInfo獲取所有客戶端的信息。ClientInfo中保存了如下信息:

  • 客戶端進(jìn)程pid和句柄
  • 生成Minidump的類型
  • 自定義的客戶端信息
  • 客戶端崩潰的線程ID
  • 客戶端崩潰的信息
  • 客戶端請(qǐng)求崩潰所使用的事件句柄

這里有一個(gè)問題:在客戶端發(fā)生崩潰時(shí),服務(wù)器如何通過ClientInfo獲取到客戶端的崩潰信息呢?

客戶端中有幾個(gè)用于保存崩潰信息的變量,在注冊(cè)時(shí),客戶端會(huì)將這幾個(gè)變量的地址發(fā)送至服務(wù)端,服務(wù)端將其保存在ClientInfo中,然后當(dāng)崩潰 發(fā)生的時(shí)候,服務(wù)端就可以通過ReadProcessMemory讀取客戶端中的信息,從而生成dump。這樣做就避免了每次發(fā)生崩潰,都要通過Pipe 將崩潰信息傳遞到服務(wù)端中去了。

這些變量分別是:崩潰的線程ID,EXCEPTION_POINTERS和MDRawAssertionInfo。
EXCEPTION_POINTERS和MDRawAssertionInfo的區(qū)別在于,異常崩潰的信息會(huì)被寫入EXCEPTION_POINTERS,非異常崩潰(非法參數(shù)和純虛函數(shù)調(diào)用)的信息會(huì)被寫入MDRawAssertionInfo中。

dump文件的上傳

在breakpad的工程中,有一個(gè)工程叫做:crash_report_sender,里面是一個(gè)上傳崩潰文件的類,他的實(shí)現(xiàn)很簡單,他使用Windows Internet Api來完成dump文件的上傳。
在使用crash_report_sender時(shí),可以為其指定一個(gè)checkpoint_file。

1
explicit CrashReportSender(const wstring &checkpoint_file);

這個(gè)文件只有一個(gè)作用,就是用來保存上次上傳崩潰的時(shí)間和今天上傳過的崩潰的次數(shù)。通過這個(gè)文件,我們就可以來設(shè)置每日上傳的崩潰的最大數(shù)量。

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;
  }
 
  // 上傳文件部分代碼,省略
}

調(diào)整每日上傳崩潰的最大數(shù)量的函數(shù)是set_max_reports_per_day。

需要注意的是:在上傳dump文件的時(shí)候,crash_report_sender并不會(huì)對(duì)dump文件進(jìn)行分析,而是直接上傳整個(gè)dump文件, 如果你需要上傳的dump文件非常大的話,可以考慮把崩潰分析處理的邏輯放入前臺(tái),通過去重或者直接上傳分析結(jié)果,減少上傳的文件大小。

breakpad存在的問題

進(jìn)程外生成dump有很多好處,其中最大的好處就是不會(huì)被崩潰進(jìn)程影響,這樣dump的過程就不容易出錯(cuò),但是這樣也有一定的弊端。

1. 部分崩潰無法抓取
在一些極端的崩潰,如堆棧溢出之類的崩潰,進(jìn)程外抓取dump有時(shí)候會(huì)失敗。

2. 無法抓取死鎖或者其他原因?qū)е碌倪M(jìn)程僵死
breakpad現(xiàn)在沒有檢測進(jìn)程死鎖的代碼,也沒有在服務(wù)端控制客戶端請(qǐng)求dump的代碼,所以現(xiàn)在breakpad無法抓取死鎖等進(jìn)程僵死的問題。不過因?yàn)閎reakpad的定位是處理崩潰,如果有這種需要的童鞋,可以自行修改breakpad的代碼,添加這些功能。

3. 對(duì)服務(wù)端有依賴
如果指定了在使用進(jìn)程外抓取dump,breakpad對(duì)服務(wù)端就有依賴。主要體現(xiàn)在抓取dump時(shí),如果服務(wù)端不存在,客戶端將無法正常抓取dump,甚至有時(shí)會(huì)出現(xiàn)阻塞。

當(dāng)然對(duì)于這些問題,隨著breakpad的發(fā)展肯定會(huì)越來越完善。如果,你遇到了了這些問題,而又繞過不了,那就改代碼,并且提交給breakpad吧,開源項(xiàng)目就是這么發(fā)展的。

好,到此breakpad的Windows實(shí)現(xiàn)就已經(jīng)說完了,如果有神馬問題,還請(qǐng)多多指教。謝謝大家。

北京德勝門中醫(yī)院http://www.0531jsk.com/德勝門中醫(yī)院

posted @ 2011-11-07 21:36 RTY 閱讀(2447) | 評(píng)論 (0)編輯 收藏

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

2011年01月23日 — Asp J

Table of contents for Google Breakpad 完全解析

  1. Google Breakpad 完全解析(一) —— Windows入門篇
  2. Google Breakpad 完全解析(二) —— Windows前臺(tái)實(shí)現(xiàn)篇

原創(chuàng)文章,轉(zhuǎn)載請(qǐng)標(biāo)明出處:Soul Apogee (http://bigasp.com),謝謝。

Google breakpad是 一個(gè)非常實(shí)用的跨平臺(tái)的崩潰轉(zhuǎn)儲(chǔ)和分析模塊,他支持Windows,Linux和Mac和Solaris。由于他本身跨平臺(tái),所以很大的減少我們?cè)谄脚_(tái)移 植時(shí)的工作,畢竟崩潰轉(zhuǎn)儲(chǔ),每個(gè)平臺(tái)下都不同,使用起來很難統(tǒng)一,而Google breakpad就幫我們做到了這一點(diǎn),不管是哪個(gè)平臺(tái)下的崩潰,都能夠進(jìn)行統(tǒng)一的分析。現(xiàn)在很多工程都在使用他:最著名的幾個(gè)如 Chrome,F(xiàn)irefox,Picasa和Google Earth。另外他的License是BSD的,也就是說,我們即便是在商業(yè)軟件中使用,也是合法的,哈哈,這么好的東西,我們能放過么?現(xiàn)在就讓我們來 看看這個(gè)神奇的軟件吧。

原理簡介

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

breakpad把應(yīng)用程序分成三個(gè)部分,代碼,breakpad客戶端和調(diào)試信息。

1. 在build system中,通過symbol dumper用平臺(tái)相關(guān)的調(diào)試信息生成平臺(tái)無關(guān)的symbol文件。這樣做的好處很明顯,一旦平臺(tái)無關(guān)了,所有平臺(tái)的崩潰就可以做統(tǒng)一的分析了。
2. breakpad采取進(jìn)程外轉(zhuǎn)儲(chǔ)和分析崩潰的方式,他使用C/S結(jié)構(gòu),客戶端用來捕獲當(dāng)前進(jìn)程中發(fā)生的崩潰,并通知服務(wù)端崩潰發(fā)生。服務(wù)端用來響應(yīng)客戶端,抓取dump文件。這樣做的目的是為了減少崩潰進(jìn)程對(duì)dump的影響。
3. Dump生成后轉(zhuǎn)發(fā)到崩潰分析器中,這個(gè)部分可以在本地也可以在服務(wù)器上,他對(duì)Dump文件進(jìn)行解析,生成可讀的堆棧信息。

這就是breakpad處理dump大概的流程。

對(duì)于原理的介紹google寫的已經(jīng)相當(dāng)好了。更多的詳細(xì)信息,可以直接移步到breakpad的wiki

安裝和編譯

breakpad的編譯比較曲折,所以在此記錄一下。

編譯breakpad,請(qǐng)確認(rèn)你的機(jī)器上裝有以下的軟件:
1. python 2.4.3
請(qǐng)不要使用python3,會(huì)報(bào)錯(cuò)。另外python2中推薦這個(gè)版本,使用新的版本在編譯其他google的工程時(shí)有時(shí)會(huì)報(bào)錯(cuò)

2. Windows SDK 7
如果沒有這個(gè),編譯會(huì)報(bào)錯(cuò)。另外這個(gè)是在線安裝,時(shí)間很久,最好并行做其他的事情。

3. VS2005的補(bǔ)丁
KB918559
KB926601
KB935225
KB943969
KB947315

已經(jīng)安裝了以上軟件的童鞋,就可以開始進(jìn)行下面的工作鳥

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. 設(shè)置Windows SDK 7
裝過其他版本W(wǎng)indows SDK的童鞋,記得一定要進(jìn)行這一步,SDK的安裝程序,并不會(huì)幫你設(shè)置VS。
運(yùn)行開始菜單->程序->Microsoft Windows SDK v7.0->Visual Studio Registration->Windows SDK Configuration Tool,選擇v7.0,點(diǎn)擊Make Current。

3. 為python設(shè)置環(huán)境變量
由于breakpad使用python來生成Windows下的工程文件,所以需要將python所在目錄,設(shè)置到環(huán)境變量PATH中去。

4. 生成Windows工程文件

1
2
3
4
cd "源碼目錄/src/tools/gyp"
 
# 注意,此處不能使用全路徑,不然會(huì)出錯(cuò)
gyp.bat "../../client/windows/breakpad_client.gyp"

此時(shí),在src/client/windows下就可以看到生成好的breakpad_client.sln了。運(yùn)行吧!

5. Hello World!
編譯build all,現(xiàn)在一般是不會(huì)報(bào)錯(cuò)了,如果報(bào)錯(cuò),請(qǐng)檢查是不是漏了什么步驟,特別是補(bǔ)丁。
編譯完成之后,運(yùn)行crash_generation_app吧,這是他的測試程序,dump的默認(rèn)位置保存在C:Dumps下,請(qǐng)注意先建立好目錄,不然會(huì)無法使用。
啟動(dòng)測試程序之后,此時(shí)還不能抓取dump,因?yàn)檫@個(gè)是breakpad中的服務(wù)器端,需要再啟動(dòng)一個(gè)測試程序,在第二個(gè)測試程序中,我們就可以試驗(yàn)Client菜單中的各種崩潰了。這些崩潰都會(huì)被抓住轉(zhuǎn)存到C:Dumps目錄下。

如何使用breakpad

在Windows下使用breakpad的方法很簡單,只需要?jiǎng)?chuàng)建一個(gè)ExceptionHandler的類即可,大家可以在crash_generation_app這個(gè)工程中找到示例代碼,也可以直接移步Wiki,上面說的也很詳細(xì)。

1.進(jìn)程內(nèi)抓取Dump文件

進(jìn)程內(nèi)抓取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.進(jìn)程外抓取Dump文件

使用進(jìn)程外抓取Dump時(shí),需要指定服務(wù)端和客戶端,在服務(wù)端中需要?jiǎng)?chuàng)建CrashGenerationServer的實(shí)例,而在客戶端中則只需要?jiǎng)?chuàng)建ExceptionHandler即可。此外,如果服務(wù)端自己需要抓進(jìn)程內(nèi)的Dump,請(qǐng)將pipe的參數(shù)置為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;
    }
 
    // 如果已經(jīng)服務(wù)端已經(jīng)啟動(dòng)了,此處啟動(dòng)會(huì)失敗
    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, // 如果是服務(wù)端,則直接使用進(jìn)程內(nèi)dump
        NULL);
 
    if(pCrashHandler == NULL) {
        return false;
    }
 
    return true;
}

使用breakpad的時(shí)候,有兩個(gè)地方需要注意:
1. 記得把breakpad的solution下的幾個(gè)工程,包含到你開發(fā)的工程中,或者直接包含他們的lib。
common:基礎(chǔ)功能,包含一個(gè)對(duì)GUID的封裝和http上傳的類。
exception_handler:用來捕獲崩潰的類。
crash_generation_server:breakpad的服務(wù)端,用來在產(chǎn)生崩潰時(shí)抓取dump。
crash_generation_client:breakpad的客戶端,用來捕獲當(dāng)前進(jìn)程的崩潰。

2. 在初始化breakpad之前,記得先創(chuàng)建好dump文件的目錄,不然breakpad服務(wù)端將不能正常的寫dump,這會(huì)導(dǎo)致breakpad客戶端在崩潰時(shí)無限等待服務(wù)端dump寫完的消息,最后失去響應(yīng)。

posted @ 2011-11-07 21:34 RTY 閱讀(1839) | 評(píng)論 (0)編輯 收藏

調(diào)試Release發(fā)布版程序的Crash錯(cuò)誤 (轉(zhuǎn))

調(diào)試Release發(fā)布版程序的Crash錯(cuò)誤

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

 

Windows平臺(tái)下用C++開發(fā)應(yīng)用程序,最不想見到的情況恐怕就是程序崩潰,而要想解決引起問題的bug,最困難的應(yīng)該就是調(diào)試release版本了。因?yàn)閞elease版本來就少了很多調(diào)試信息,更何況一般都是發(fā)布出去由用戶使用,crash的現(xiàn)場很難保留和重現(xiàn)。本文將給出幾個(gè)解決方案,完成對(duì)release版應(yīng)用程序crash錯(cuò)誤的調(diào)試。(本文只討論Windows平臺(tái)MSVC環(huán)境下的調(diào)試,對(duì)于其他平臺(tái)和開發(fā)環(huán)境沒有關(guān)注,請(qǐng)大家自己借鑒和嘗試。)

 

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

    這種方案只能對(duì)VC7以前的版本開發(fā)的程序使用。 

    1、崩潰地址

     所謂崩潰地址就是引起程序崩潰的內(nèi)存地址,在WinXP下應(yīng)用程序crash的對(duì)話框如下圖:

clip_image001

clip_image002

clip_image003

    上面第2張圖中畫紅線的值為crash的代碼偏移地址,第3張圖為即crash絕對(duì)地址;一般引起crash的原因多為內(nèi)存操作錯(cuò)誤,我們用這兩個(gè)地址和MAP文件就能定位出錯(cuò)的代碼行。

    2MAP文件

    MAP文件是記錄應(yīng)用程序信息的文件(文本文件),里面大概包含了程序的全局符號(hào)、源碼模塊名、源碼文件和行號(hào)等信息,而這些信息能夠幫助我們定位出錯(cuò)的代碼行。

    怎樣生成MAP文件呢?以VC6為例,在 Project Settings -> C/C++ -> Debug info中,選擇 Line Numbers Only ;在 Project Settings -> Link 中,選擇 Generate mapfile項(xiàng),并在Project Options 里面輸入 /MAPINFO:LINES/MAPINFO:EXPORTS,重新編譯程序就會(huì)生成.map文件。

    以上設(shè)置對(duì)應(yīng)的編譯鏈接選項(xiàng)分別分:

    /Zi — 表示生成pdb調(diào)試信息;

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

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

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

    由于/MAPINFO:LINES選項(xiàng)在VC8以后的版本中不再支持,因此通過MAP文件中的信息和crash地址定位出錯(cuò)代碼行就比較困難了,所以這種方案只能在VC7及以前的版本中使用。

    一個(gè)MAP文件片段示例如下: 

    clip_image004  

    clip_image005

    圖中Rva+Base列的地址為該行函數(shù)對(duì)應(yīng)的函數(shù)絕對(duì)地址,Address列中冒號(hào)后面的地址為函數(shù)相對(duì)偏移地址。   

    3、定位crash代碼

    有了上面的介紹,定位crash代碼就很簡單了。用下面的公式來進(jìn)行定位:

    崩潰行偏移 = 崩潰地址 - 崩潰函數(shù)絕對(duì)地址 + 函數(shù)相對(duì)偏移

    我們首先根據(jù)崩潰地址(絕對(duì)地址),按照找到第2張圖中Rva+Base列的地址找到發(fā)生崩潰的函數(shù)(即崩潰地址大于該函數(shù)行的Rva+Base地址且小于下個(gè)函數(shù)的地址),然后找到該行對(duì)應(yīng)的函數(shù)相對(duì)偏移地址,帶入公式中,就得到了崩潰行偏移,該值表示崩潰行的代碼相對(duì)于代碼所在函數(shù)的偏移量。用該值去與第3張圖中對(duì)應(yīng)函數(shù)冒號(hào)后面的偏移量去比較,最接近的值前面的那個(gè)十進(jìn)制數(shù)即為代碼所在函數(shù)中的行號(hào)。

    ok,到此我們已經(jīng)成功找到了崩潰的代碼行,只不過這種方法還是比較費(fèi)力,并且限制比較多,我們看看下面的方案。

上篇給出的方案一還要補(bǔ)充幾句。通過“crash地址 + MAP文件”來定位出錯(cuò)代碼位置雖然需要經(jīng)過比較復(fù)雜的地址計(jì)算,但卻是最簡單實(shí)現(xiàn)的方式。如果僅僅想通過崩潰地址定位出錯(cuò)的函數(shù),就更加方便了。我在網(wǎng)上找到一個(gè)解析MAP文件的小工具,可以非常清晰的列出每個(gè)函數(shù)的地址,并且可以將分析表格導(dǎo)出為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文件中產(chǎn)生代碼行信息,因此我們尋找另一種定位方式:COD文件。

    1COD文件

    COD文件是一個(gè)包含了匯編碼、二進(jìn)制機(jī)器碼和源代碼對(duì)應(yīng)信息的文件,每一個(gè)cpp都對(duì)應(yīng)一個(gè)COD文件。通過這個(gè)文件,我們可以非常方便地進(jìn)行定位。

   VC6中生成COD文件的設(shè)置方式為:Project Settings -> C/C++,在 Category 中選 Listing Files,在 Listing file type 組合框中選 Assembly,Machine code,and source。在VC8中生成COD文件的設(shè)置方式為:Project Properties -> C/C++ -> Output Files -> Assembler Output 項(xiàng),選擇 Assembly,Machine code,and Source(/Facs)。

   

    2、定位崩潰行

    下面通過舉例進(jìn)行說明。現(xiàn)在我有一個(gè)基于對(duì)話框的MFC應(yīng)用程序CrashTest,在CCrashTestDlg::OnInitDialog函數(shù)中寫入導(dǎo)致crash的代碼語句(第99行),源文件如下:

    clip_image006

    根據(jù)崩潰地址(0x004012A3)以及MAP文件(定位片段圖片如下),定位crash函數(shù)為OnInitDialog;并且我們可以很容易地計(jì)算出崩潰地址相對(duì)于崩潰函數(shù)的偏移量為 0x004012A3 - 0x004011E0 = 0xC3。

    clip_image007

    再來看看CrashTestDlg.cod文件,我們根據(jù)文件中源碼信息找到OnInitDialog函數(shù)信息片段:

    clip_image008

    可以看到圖片中第一行為OnInitDialog函數(shù)匯編代碼的起始行;找到“int * p = NULL;”這一句源碼,其前面的98表示這行代碼在源文件中的行號(hào),下面的000c1表示相對(duì)于函數(shù)開始位置的偏移量,后面的“33 c0”為機(jī)器碼,“xor eax,eax”為匯編碼。那么我們根據(jù)前面算出來的偏移量0xC3,找到對(duì)應(yīng)出錯(cuò)的語句為99行:“*p = 5;”。

    總結(jié)一下定位步驟:

    1) 根據(jù)公式 崩潰語句在函數(shù)中偏移地址 = 崩潰地址 - 崩潰函數(shù)地址 計(jì)算出偏移量X;

    2) 根據(jù)公式 崩潰語句在COD文件中地址 = 崩潰函數(shù)在COD文件中地址 + X 計(jì)算出地址Y。其中崩潰函數(shù)在COD文件中地址為COD文件中函數(shù)起始括號(hào)“{”后面表明的地址,一般情況下為0x0000;

    3) 根據(jù)Y在COD文件中找到對(duì)應(yīng)代碼行。

   

    ok,方案二介紹完了。這種方法最大的好處是沒有VC開發(fā)環(huán)境版本限制,而且COD文件里面包含的信息更加豐富,不但可以幫助我們定位crash,還能幫我們分析很多東西。當(dāng)然,這也導(dǎo)致編譯生成了很多信息文件。

根據(jù)前面兩篇博文,我們要定位崩潰行代碼,必須要自己根據(jù)相關(guān)信息文件進(jìn)行計(jì)算。如果需要處理的量比較大,恐怕會(huì)很費(fèi)力氣。有沒有更簡單快速的辦法呢?

    最直接的想法就是寫一個(gè)小工具,根據(jù)規(guī)則和信息進(jìn)行自動(dòng)定位,不過開發(fā)起來也是要費(fèi)一番功夫的。令人開心的是,我們可以找到類似的工具,而且是開源免費(fèi)的!程序員的世界也許很多時(shí)候都是這么單純而樂于分享!

   

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

    CrashFinder是一個(gè)開源工具,作者是John Robbin,大家可以去他的blog上去找關(guān)于CrashFinder的信息。我們這里以CrashFinder2.5版本為例介紹,相關(guān)文章鏈接為:http://www.wintellect.com/CS/blogs/jrobbins/archive/2006/04/19/crashfinder-returns.aspx

    1PDB文件

    PDB(Program Database)文件中包含了exe程序所有的調(diào)試相關(guān)信息,具體可以查閱MSDN。當(dāng)編譯選項(xiàng)設(shè)置為/Zi,鏈接選項(xiàng)設(shè)置為/DEBUG,/OPT:REF時(shí),就會(huì)生成工程的.pdb文件。具體到VC2005中,就是 Project Propertise -> C/C++ -> General -> Debug Information Format 項(xiàng)設(shè)置為 Program Database(/Zi),Linker -> Debugging -> Generate Debug Info 項(xiàng)設(shè)置為 Yes(/Debug),Linker -> Optimization -> References 項(xiàng)設(shè)置為 Eliminate Unreferenced Data(/OPT:REF)。

    只要設(shè)置以上選項(xiàng),release版本也能生成PDB文件。當(dāng)然,對(duì)應(yīng)的應(yīng)用程序也會(huì)稍大。

    2CrashFinder

    CrashFinder能夠運(yùn)行需要兩個(gè)條件:一是系統(tǒng)必須要有dbghelp.dll文件;二是PDB文件必須與exe文件在一個(gè)路徑下。對(duì)于dbghelp.dll,一般在系統(tǒng)system32路徑下都有,如果沒有下載一個(gè)放到這個(gè)目錄下就可以了。

    先看一下CrashFinder的界面。

   

clip_image009

    用起來也非常簡單。首先選擇File->New或點(diǎn)擊工具欄新建按鈕,選擇要調(diào)試的exe文件打開,會(huì)發(fā)現(xiàn)exe及所依賴的dll文件信息都已經(jīng)加載進(jìn)來。在下半部分的編輯框中輸入崩潰地址(16進(jìn)制),點(diǎn)右邊的“Find”按鈕,就會(huì)在下面顯示崩潰的源文件路徑、名稱以及崩潰所在行號(hào)了,如下圖所示。

clip_image010

   CrashFinder進(jìn)行crash定位真的非常方便。但是我在使用過程中發(fā)現(xiàn)了一個(gè)bug,每次啟動(dòng)程序后,直接新建的話加載進(jìn)來的exe模塊都顯示叉,提示找不到debug symbols。但是用打開按鈕隨便打開一個(gè)文件失敗后,再新建就能成功。猜測可能是直接新建,定位PDB文件時(shí)的路徑不對(duì)引起的。有源碼,但是懶的看了呵呵,大家感興趣可以試一下。

    好了,方案三就介紹到這里,后面還有更加強(qiáng)大的方案 : )

前面幾個(gè)方案都是直接定位crash的代碼位置,但是在比較大型的程序中,只知道這個(gè)信息還是遠(yuǎn)遠(yuǎn)不夠的,我們希望知道更多關(guān)于調(diào)用函數(shù)順序及變量值等信息,也就是crash時(shí)調(diào)用堆棧信息。

 

    方案四:SetUnhandledExceptionFilter + StackWalker

    這個(gè)方案需要自己動(dòng)手往工程里添加代碼了。要實(shí)現(xiàn)上面的想法,需要做兩件事情:1、需要在crash時(shí)有機(jī)會(huì)對(duì)程序堆棧進(jìn)行處理;2、對(duì)堆棧信息進(jìn)行收集。

    1SetUnhandleExceptionFilter函數(shù)

    Windows平臺(tái)下的C++程序異常通常可分為兩種:結(jié)構(gòu)化異常(Structured Exception,可以理解為與操作系統(tǒng)相關(guān)的異常)和C++異常。對(duì)于結(jié)構(gòu)化異常處理(SEH),可以找到很多資料,在此不細(xì)說。對(duì)于crash錯(cuò)誤,一般由未被正常捕獲的異常引起,Windows操作系統(tǒng)提供了一個(gè)API函數(shù)可以在程序crash之前有機(jī)會(huì)處理這些異常,就是SetUnhandleExceptionFilter函數(shù)。(C++也有一個(gè)類似函數(shù)set_terminate可以處理未被捕獲的C++異常。)

    SetUnhandleExceptionFilter函數(shù)聲明如下:

    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允許我們?cè)O(shè)置一個(gè)自己的函數(shù)作為全局SEH過濾函數(shù),當(dāng)程序crash前會(huì)調(diào)用我們的函數(shù)進(jìn)行處理。我們可以利用的是 _EXCEPTION_POINTERS 結(jié)構(gòu)類型的變量ExceptionInfo,它包含了對(duì)異常的描述以及發(fā)生異常的線程狀態(tài),過濾函數(shù)可以通過返回不同的值來讓系統(tǒng)繼續(xù)運(yùn)行或退出應(yīng)用程序。

    關(guān)于 SetUnhandleExceptionFilter 函數(shù)的具體用法和示例請(qǐng)參考MSDN。

 

    2StackWalker
    現(xiàn)在我們已經(jīng)有機(jī)會(huì)可以在crash之前對(duì)程序狀態(tài)信息進(jìn)行處理了,只需要生成并保存堆棧信息就大功告成了。Windows的dbghelp.dll庫提供了一個(gè)函數(shù)可以得到當(dāng)前堆棧信息:StackWalk64(在Win2K以前版本中為StackWalk)。該函數(shù)聲明如下:

    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
    );
    該函數(shù)的具體用法可以參考MSDN。在這里推薦一個(gè)牛人寫好的StackWalker,可以直接拿來用,開源的。StackWalker提供了一個(gè)基類,給出了幾個(gè)簡單的接口,可以方便地生成堆棧信息,并且支持一系列VC版本,非常好用。我們可以自己寫一個(gè)子類,并重載虛函數(shù)OnOutput,就可以將堆棧信息輸出為特定格式了。StackWalker的地址為:http://www.codeproject.com/KB/threads/StackWalker.aspx

    不過對(duì)于Release版本來說,StackWalk64函數(shù)獲得的堆棧信息有可能不完整。如果異常是由MFC的模塊拋出,那么獲得的堆棧可能缺少前面調(diào)用模塊信息。另外,StackWalk64需要最新的dbghelp.dll文件支持才能工作;要正確輸出crash的函數(shù)名和行號(hào),需要要pdb文件支持。以上不足有可能影響輸出信息的完整性和效果,而對(duì)于發(fā)布在外的程序,要帶上pdb文件幾乎不可能,因此這個(gè)方案還是有缺憾的,比較適用于本地的release版本調(diào)試。

    下一篇我們將介紹一個(gè)更加完善的解決方案

當(dāng)我們把自己的release版本程序發(fā)布出去以后,一般都是在用戶的機(jī)器上運(yùn)行。這種情況下,對(duì)于第四種方案,因?yàn)樾枰猵db文件才能夠正確生成堆棧調(diào)用的函數(shù)行號(hào)及代碼行號(hào),因此方案四只適用于本地release版的調(diào)試,否則只能生成不完整的堆棧信息。對(duì)于前三種方案,其實(shí)只需要用戶告知崩潰地址,然后在本地查找crash地址就可以了,但是定位crash的過程非常不方便,如果crash的情況比較多,前三種方案都不合適。而且,前三種方案均不能生成堆棧調(diào)用信息,對(duì)于debug的作用有限。

    下面我們就來看一個(gè)更加完善的解決方案。

 

    方案五:SetUnhandledExceptionFilter + Minidump

    SetUnhandleExceptionFilter函數(shù)我們已經(jīng)介紹過了,本方案的思路還是要利用我們自己的異常處理函數(shù),來生成minidump文件。

    1Minidump概念

    minidump(小存儲(chǔ)器轉(zhuǎn)儲(chǔ))可以理解為一個(gè)dump文件,里面記錄了能夠幫助調(diào)試crash的最小有用信息。實(shí)際上,如果你在 系統(tǒng)屬性 -> 高級(jí) -> 啟動(dòng)和故障恢復(fù) -> 設(shè)置 -> 寫入調(diào)試信息 中選擇“小內(nèi)存轉(zhuǎn)儲(chǔ)(64 KB)”的話,當(dāng)系統(tǒng)意外停止時(shí)都會(huì)在C:\Windows\Minidump\路徑下生成一個(gè).dmp后綴的文件,這個(gè)文件就是minidump文件,只不過這個(gè)是內(nèi)核態(tài)的minidump。

   我們要生成的是用戶態(tài)的minidump,文件中包含了程序運(yùn)行的模塊信息、線程信息、堆棧調(diào)用信息等。而且為了符合其mini的特性,dump文件是壓縮過的。

    2、生成minidump文件

    生成minidump文件的API函數(shù)是MiniDumpWriteDump,該函數(shù)需要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
    );

    在我們的異常處理函數(shù)中加入以下代碼:

    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變量為異常處理函數(shù)PEXCEPTION_POINTERS類型的參數(shù)。具體請(qǐng)參考MSDN。

    3、調(diào)試minidump

    調(diào)試dump文件首先需要pdb文件,因此我們build程序時(shí)需要設(shè)置 Debug Infomation Format 為 “Program Database(/Zi)”。其次,我們還要確保所用的dump文件與源代碼、exe、pdb文件版本是一致的,這要求我們必須維護(hù)好程序版本信息。

    調(diào)試minidump最方便的環(huán)境就是VS了,我們只要將.dmp、.exe、.pdb文件放在一個(gè)路徑下,保證源代碼文件的路徑與編譯時(shí)的路徑一致就可以了,剩下的就是VS幫我們完成。雙擊.dmp文件或者在文件打開工程中選擇“dump files”,加載dump文件,然后按F5運(yùn)行就能直接恢復(fù)crash時(shí)的現(xiàn)場了,你可以定位crash的代碼,可以查看調(diào)用堆棧,可以查看線程和模塊信息...一切都跟你設(shè)置斷點(diǎn)調(diào)試一樣,太強(qiáng)大了!看個(gè)截圖吧。

clip_image012

    需要注意的是,對(duì)于release版的程序來說,很多代碼是經(jīng)過編譯器優(yōu)化過的,因此定位的時(shí)候可能會(huì)有所偏差,大家可以考慮設(shè)置選項(xiàng)去掉代碼優(yōu)化。

    其他可以調(diào)試minidump的工具還有WinDbg等,大家可以查閱相關(guān)資料。

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

    下一篇,我們將給出一個(gè)調(diào)試release發(fā)布程序的完美解決方案,適合用戶量較大的應(yīng)用發(fā)布程序的調(diào)試。

上一篇我們已經(jīng)給出了方案,能夠非常方便的通過dump文件對(duì)crash錯(cuò)誤進(jìn)行調(diào)試和定位;從整個(gè)流程上看還差最后一步,即怎樣拿到crash時(shí)產(chǎn)生的dump文件。如果可以讓用戶把文件發(fā)送過來自然不錯(cuò),但對(duì)于類似免費(fèi)共享軟件等在互聯(lián)網(wǎng)上發(fā)布的程序呢?我們的用戶是不確定的,而且用戶量有可能非常大,即使我們能想辦法聯(lián)系到用戶,總不能挨個(gè)去收集crash信息吧。

    我們需要一種方案,能夠提供crash信息匯報(bào)功能。

    我們可以架設(shè)一臺(tái)服務(wù)器專門進(jìn)行信息收集,只要客戶端在crash時(shí)正確匯報(bào)即可,但是相應(yīng)的維護(hù)成本和開發(fā)難度也不可忽視。有沒有更簡單的方法呢?還記得我的博文“為程序添加自動(dòng)發(fā)送Email功能嗎?這就是簡單有效的方法!

 

    方案六:minidump + email

    我們只需要在異常處理時(shí),先生成minidump信息文件,再用email方式將文件發(fā)送到指定郵箱就行了。剩下的就是我們每天查看郵箱,提取dump文件進(jìn)行調(diào)試了。

    1Email功能

    首先我們來看一下email發(fā)送都需要哪些相關(guān)信息。

    a、發(fā)送端郵箱帳戶;

    b、接收端郵箱帳戶;

    c、email標(biāo)題,一般應(yīng)有軟件名稱及版本信息;

    d、email正文,一般應(yīng)有簡單的crash信息提示,以區(qū)別不同原因造成的crash;

    e、email附件,當(dāng)然就是我們的dump文件了,還可以加上軟件生成的log文件等。

    當(dāng)然,對(duì)于標(biāo)題應(yīng)該盡量多加一些信息區(qū)別引起crash的原因,比如將crash的地址信息加到標(biāo)題中;因?yàn)楫?dāng)每天有成百上千的crash匯報(bào)上來,重復(fù)的crash占大多數(shù),把時(shí)間都花在區(qū)分它們身上有點(diǎn)太浪費(fèi)。由此看來,前面方案中提到的StackWalker還是有些用處的,我們可以用它來生成一些crash的文字描述信息,寫到標(biāo)題或正文中去。

    dump文件的大小是否適合作為郵件的附件呢?實(shí)際上minidump產(chǎn)生的文件一般在幾K到幾十K之間,作為email的附件沒有任何問題。

    關(guān)于發(fā)送email相關(guān)技術(shù)細(xì)節(jié),已經(jīng)在“為程序添加自動(dòng)發(fā)送Email功能文中介紹了,大家可以參考。其實(shí),對(duì)接受郵箱中郵件的處理還是很費(fèi)時(shí)費(fèi)力的,大家可以考慮寫一些腳本將處理流程自動(dòng)化,提高效率。

    2google breakpad

    google breakpad是一個(gè)開源的跨平臺(tái)crash report系統(tǒng),光從開源和跨平臺(tái)這兩個(gè)特點(diǎn)上來看,它就足以稱的上是一個(gè)完善而有效的工具了。其實(shí),breakpad在整個(gè)crash report層次上給出了一個(gè)系統(tǒng)級(jí)的解決方案,也就是說它幾乎能適應(yīng)各種軟件、各種平臺(tái)的應(yīng)用要求。

    breakpad的整體思路跟上面介紹的方案是相似的,只不過最后提交dump文件的方式更加完善。大家有興趣可以去它的官方網(wǎng)址查閱相關(guān)資料:http://code.google.com/p/google-breakpad/

 

    ok,關(guān)于調(diào)試release發(fā)布程序的crash錯(cuò)誤系列文章就寫完了。這幾篇文章給出的方案由簡單到復(fù)雜,由簡陋到完善,對(duì)crash調(diào)試有了一個(gè)比較全面的總結(jié)。當(dāng)然,其中涉及到的概念和技術(shù)還很多,需要我們?nèi)ゲ粩鄬W(xué)習(xí)和領(lǐng)悟,也希望大家能夠互相交流。

posted @ 2011-11-07 21:15 RTY 閱讀(1264) | 評(píng)論 (0)編輯 收藏

僅列出標(biāo)題
共31頁: First 3 4 5 6 7 8 9 10 11 Last 
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久久国产精品亚洲一区 | 亚洲国产精品久久精品怡红院| 欧美成人自拍| 欧美激情一区二区久久久| 欧美性大战久久久久久久| 国产精品高潮粉嫩av| 欧美日韩一区二区在线播放| 欧美体内谢she精2性欧美| 国产精品亚洲а∨天堂免在线| 欧美性大战久久久久| 国产精品美女久久久久久久| 国产亚洲欧美一级| 在线观看日韩国产| 中文日韩欧美| 欧美在线播放高清精品| 久久精品人人爽| 亚洲国产小视频在线观看| 亚洲精品久久视频| 日韩视频一区二区三区在线播放免费观看| 欧美大片免费观看| 欧美高清一区二区| 一本色道婷婷久久欧美| 性欧美大战久久久久久久久| 欧美成人在线免费视频| 国产精品无码专区在线观看| 在线精品视频免费观看| 午夜精品一区二区三区在线播放| 久久国产精品一区二区| 亚洲日本欧美天堂| 久久国产精品黑丝| 欧美久久久久久久久| 国产欧美一区二区视频| 国产欧美韩国高清| 久久一二三四| 国产日韩精品一区二区浪潮av| 亚洲免费不卡| 久久久久女教师免费一区| 99这里只有久久精品视频| 久久全国免费视频| 国产精品视频99| 亚洲午夜精品国产| 亚洲国产成人精品久久| 久久岛国电影| 国产午夜精品一区二区三区欧美| 亚洲精品永久免费精品| 巨乳诱惑日韩免费av| 亚洲图片在区色| 欧美日韩一区二区在线| 亚洲精品综合| 久久综合久久综合久久综合| 亚洲午夜精品一区二区三区他趣| 欧美伦理91| 99精品视频免费| 欧美黑人在线观看| 免费视频一区| 亚洲国产影院| 欧美激情小视频| 免费成人你懂的| 在线观看亚洲一区| 美乳少妇欧美精品| 久久日韩粉嫩一区二区三区| 激情综合激情| 欧美成人精品激情在线观看| 久久久久久成人| 在线成人激情黄色| 欧美激情一区二区三区高清视频 | 欧美阿v一级看视频| 在线播放亚洲一区| 模特精品裸拍一区| 久久久.com| 久久伊人一区二区| 亚洲一级特黄| 亚洲专区一区二区三区| 国产精品日韩二区| 欧美精品久久久久久久免费观看| 国际精品欧美精品| 久久这里只有精品视频首页| 午夜老司机精品| 国产一区在线视频| 久久色在线播放| 久久九九热免费视频| 亚洲人成网站777色婷婷| 能在线观看的日韩av| 欧美成人a视频| 亚洲高清在线观看| 日韩视频不卡| 国产精品美女久久久久aⅴ国产馆| 亚洲午夜一区二区| 亚洲中午字幕| 亚洲国产成人91精品| 亚洲国产精品第一区二区三区| 欧美激情一区二区三区在线视频| 亚洲色图在线视频| 99热在这里有精品免费| 国产区二精品视| 久久综合久久综合久久| 欧美成人精品在线| 久久成人免费网| 欧美精品电影在线| 欧美在线一区二区三区| 六月天综合网| 亚洲欧美一区在线| 你懂的网址国产 欧美| 亚洲字幕一区二区| 亚洲国产午夜| 国产日韩在线视频| 日韩视频二区| 亚洲成色精品| 亚洲女爱视频在线| 一本色道久久综合亚洲精品不卡| 欧美一区成人| 亚洲一区二区三区中文字幕| 久久一区二区三区四区| 欧美一区二区三区视频免费播放| 欧美成人午夜激情| 久久精品国产一区二区三区| 欧美国产免费| 久久一区二区三区av| 国产精品s色| 亚洲黄一区二区| 狠狠色丁香婷综合久久| 99国产精品久久久久久久成人热| 精品白丝av| 亚洲一区二区三区中文字幕| 日韩午夜在线电影| 欧美成人69av| 亚洲电影免费| 亚洲国产精品久久久久秋霞影院| 午夜精品久久久久久久蜜桃app| 亚洲一区二区三区精品在线| 免费在线观看日韩欧美| 老巨人导航500精品| 国产一区欧美| 欧美一区午夜精品| 久久黄色小说| 国产偷久久久精品专区| 亚洲与欧洲av电影| 亚洲日本欧美| 国产精品欧美久久| 99精品99久久久久久宅男| 在线观看欧美成人| 久久精品女人天堂| 久久久久久香蕉网| 激情文学综合丁香| 女生裸体视频一区二区三区| 亚洲第一区中文99精品| 亚洲日本中文字幕免费在线不卡| 老牛嫩草一区二区三区日本| 欧美大片在线观看一区二区| 亚洲激情啪啪| 欧美日韩在线精品| 一本高清dvd不卡在线观看| 亚洲午夜黄色| 国产区欧美区日韩区| 国产亚洲成年网址在线观看| 免费黄网站欧美| 亚洲第一精品电影| 欧美黄色日本| 亚洲视频在线一区| 午夜精品久久久| 狠狠爱www人成狠狠爱综合网| 久久久久久网站| 亚洲人成网站精品片在线观看| 一区二区三区欧美| 国产精品尤物福利片在线观看| 久久er精品视频| 亚洲日韩成人| 久久av红桃一区二区小说| 在线不卡视频| 欧美日韩国产综合网| 亚洲一区二区三区三| 久久黄色小说| 国产日产欧美a一级在线| 久久婷婷国产综合精品青草| 日韩一二在线观看| 久久久久久久久蜜桃| 亚洲欧洲日产国码二区| 国产精品日本一区二区 | 麻豆精品视频在线观看| 亚洲三级免费观看| 久久精品欧美日韩| 亚洲一区二区三区精品动漫| 亚洲高清视频一区二区| 国产精品日韩在线播放| 欧美大片在线观看| 久久精品视频在线| 亚洲在线观看免费| 日韩视频在线免费观看| 美女久久网站| 欧美在线亚洲| 亚洲一区国产一区| 99国产精品久久久久久久| 激情丁香综合| 国产亚洲福利| 国产精品一区一区| 欧美日韩第一区| 欧美第一黄网免费网站| 久久国产精品99国产精| 午夜精品久久99蜜桃的功能介绍| 99精品国产福利在线观看免费 |