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

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

調試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調試有了一個比較全面的總結。當然,其中涉及到的概念和技術還很多,需要我們去不斷學習和領悟,也希望大家能夠互相交流。

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲欧美视频| 国产日韩欧美综合精品| 夜久久久久久| 亚洲经典自拍| 美女91精品| 欧美国产一区二区在线观看| 久久综合五月| 欧美成人一区在线| 亚洲国产精品小视频| 欧美高清在线观看| 亚洲精品系列| 亚洲综合另类| 欧美一级成年大片在线观看| 久久看片网站| 欧美成va人片在线观看| 欧美韩日精品| 欧美日韩免费观看中文| 亚洲国产成人一区| 亚洲精品日韩在线| 99国产精品自拍| 午夜精品三级视频福利| 久久精品亚洲精品| 欧美精品一区二区高清在线观看| 欧美日韩裸体免费视频| 国产欧美日韩视频在线观看| 激情综合在线| 9久re热视频在线精品| 午夜精品久久久久久久久久久久久| 羞羞答答国产精品www一本 | 日韩午夜在线电影| 一区二区三区 在线观看视| 亚洲一区二区三区影院| 久久综合伊人77777尤物| 欧美国产精品劲爆| 亚洲伊人一本大道中文字幕| 久久国内精品视频| 欧美日韩高清区| 国产在线拍偷自揄拍精品| 亚洲欧洲视频| 欧美一级大片在线免费观看| 欧美顶级艳妇交换群宴| 亚洲视频综合| 欧美va亚洲va国产综合| 国产精品剧情在线亚洲| 亚洲第一页在线| 午夜精品一区二区在线观看| 亚洲大黄网站| 久久www免费人成看片高清| 欧美理论大片| 亚洲电影免费观看高清完整版在线观看| 日韩视频永久免费观看| 久久免费精品日本久久中文字幕| 日韩午夜剧场| 老司机一区二区| 国产嫩草一区二区三区在线观看| 亚洲黄色成人网| 先锋影音久久| 99视频精品在线| 欧美大片免费观看在线观看网站推荐| 国产香蕉久久精品综合网| 中文亚洲免费| 亚洲国产片色| 看片网站欧美日韩| 国内欧美视频一区二区| 亚洲视频在线看| 亚洲国产日韩欧美| 欧美91福利在线观看| 韩国v欧美v日本v亚洲v| 欧美专区中文字幕| 亚洲欧美在线aaa| 国产精品一区免费在线观看| 亚洲深爱激情| 亚洲午夜精品网| 国产精品美女久久久久久免费| 一本色道婷婷久久欧美| 久久久综合网站| 亚洲欧美国产精品桃花 | 国产农村妇女毛片精品久久莱园子| 一区二区三区 在线观看视| 亚洲人妖在线| 欧美日本免费| 午夜精品福利电影| 亚洲欧美国产制服动漫| 国产精品成人免费视频 | 欧美日韩在线一区二区三区| 一区二区三区www| 一区二区福利| 国产欧美日韩| 免费日本视频一区| 男女av一区三区二区色多| 在线免费观看日韩欧美| 亚洲国产三级| 国产精品久久久久久久9999| 亚洲欧美国产77777| 西瓜成人精品人成网站| 在线观看成人av| 亚洲成人资源| 国产精品久久久久久久app| 欧美在线观看你懂的| 久久久久久尹人网香蕉| 亚洲精品一二| 亚洲视频精品| 国产一区日韩二区欧美三区| 免费不卡在线观看av| 欧美激情bt| 校园春色综合网| 久久亚洲私人国产精品va| 日韩视频免费大全中文字幕| 亚洲一区二区三区乱码aⅴ| 国产麻豆综合| 嫩草成人www欧美| 欧美午夜视频在线观看| 久久综合成人精品亚洲另类欧美 | 国产精品羞羞答答| 久久这里有精品视频| 欧美区亚洲区| 久久在线免费观看| 国产精品第一页第二页第三页| 久久av红桃一区二区小说| 欧美插天视频在线播放| 久久精品中文| 欧美天堂亚洲电影院在线播放| 免费日韩成人| 国产精品一级二级三级| 亚洲高清视频在线观看| 国产自产精品| 亚洲一区二区三区777| 亚洲国产一区二区在线| 欧美亚洲一区二区在线| 亚洲一区二区三区视频播放| 久久人人爽国产| 欧美一级专区| 国产精品久久一区二区三区| 亚洲精品乱码视频| 欧美激情导航| 欧美大片网址| 一区二区在线观看视频| 亚洲综合欧美| 午夜精品一区二区三区四区 | 亚洲国产小视频| 伊人精品视频| 久久精品天堂| 久久久夜夜夜| 国产一区二区三区免费不卡| 亚洲一区二区欧美| 亚洲欧美一区二区精品久久久| 欧美日韩国产不卡| 亚洲国产精品久久91精品| 国产日韩在线看| 亚洲欧美日韩久久精品| 午夜精品久久久久久99热软件| 欧美日韩国产天堂| 亚洲人www| 在线视频日本亚洲性| 欧美精品在线免费观看| 亚洲韩国精品一区| 亚洲人成啪啪网站| 免费观看一区| 亚洲国产91精品在线观看| 亚洲国产导航| 欧美劲爆第一页| 日韩亚洲欧美综合| 亚洲深夜福利在线| 欧美日韩国产三级| 一区二区三区日韩在线观看| 性欧美办公室18xxxxhd| 国产精品第一区| 亚洲在线一区二区三区| 欧美一区二区三区四区高清 | 欧美主播一区二区三区美女 久久精品人 | 免费成人av在线看| 精品99一区二区三区| 老司机精品导航| 亚洲六月丁香色婷婷综合久久| 欧美色欧美亚洲另类二区 | 欧美激情国产精品| 99re6热在线精品视频播放速度| 欧美大片va欧美在线播放| 亚洲精选91| 久久九九电影| 1204国产成人精品视频| 欧美高清视频一区二区| 在线综合亚洲| 亚洲日本乱码在线观看| 国产精品理论片在线观看| 久久久久久噜噜噜久久久精品| 亚洲第一精品电影| 亚洲与欧洲av电影| 黄色日韩网站| 国产精品久久久久久久久久尿| 久久九九国产精品| 亚洲免费成人| 久久久亚洲影院你懂的| 中日韩男男gay无套| 午夜精品福利在线| 国产在线日韩| 欧美午夜视频在线| 久久综合一区| 亚洲一区3d动漫同人无遮挡| 免费国产自线拍一欧美视频|