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

牽著老婆滿街逛

嚴以律己,寬以待人. 三思而后行.
GMail/GTalk: yanglinbo#google.com;
MSN/Email: tx7do#yahoo.com.cn;
QQ: 3 0 3 3 9 6 9 2 0 .

Microsoft Visual C++ and Win32 structured exception handling

轉載自:http://www.howzatt.demon.co.uk/articles/oct04.html

Introduction

In an earlier article [1] I described some performance measurements when using exceptions in various languages on Windows. 
A couple of people since then have asked me questions about how the windows exception model actually works and how it is used to implement try ... catch constructs in MSVC. That's quite a big question to answer, and this article is a start. There is quite a lot written about how to use these language features safely; but much less written about how they are implemented. There are several good reasons for this:
  • lessons learned about the language features themselves apply to all versions of standard C++, whatever the platform, whereas details of the implementation are specific to both the vendor and the platform.
  • knowing how it works is not necessary to using it
  • the details are very sketchily documented and not guaranteed by Microsoft to remain unchanged
On the other hand I for one like to know what is going on "under the covers" so that:
  • I can satisfy my 'how do they do that?' curiousity
  • I can understand the flow of control when trying to debug application problems
  • I can perhaps provide some platform specific value-added services.
In order to give some motivation to the investigation here's my task: I want to develop an 'exception helper' so I can print out a simple call stack for a caught C++ exception. Java and C# both let you print the stack trace for the exception, but standard C++ does not provide a way to do this. Although I understand why this feature is not part of the language I do miss it in C++ and would like to do what I can towards providing it for a specific platform, in this case MSVC. 
There are some, rather intrusive, source level solutions involving adding code to the constructors of all exception types used in your application or adding code to each use of 'throw'. This sort of solution means you must change the way exceptions are used throughout the code base which can be a large task even if you have access to all the source code, and impossible if not. 
I'll describe writing a class for the MSVC compiler so that this code:
	void testStackTrace()
	{
	    ExceptionStackTrace helper;

	    try
	    {
	        doSomethingWhichMightThrow();
	    }
	    catch ( std::exception & ex )
	    {
	        std::cerr << "Exception occurred: " << ex.what() << std::endl;
	        helper.printStackTrace( std::cerr );
	    }
	}
prints out a stack trace for the exception:
Exception occurred: A sample error...
  Frame       Code address
  0x0012FE70  0x7C57E592 RaiseException+0x55
  0x0012FEB0  0x7C359AED CxxThrowException+0x34
  0x0012FF10  0x004013CA throwIt+0x4a at teststacktrace.cpp(32)
  0x0012FF18  0x004013E8 doSomethingWhichMightThrow+0x8 at teststacktrace.cpp(37)
  0x0012FF58  0x0040142A testStackTrace+0x3a at teststacktrace.cpp(45)
  0x0012FF60  0x004014A8 main+0x8 at teststacktrace.cpp(57)
  0x0012FFC0  0x00404E87 mainCRTStartup+0x143 at crtexe.c(398)
  0x0012FFF0  0x7C581AF6 OpenEventA+0x63d

Processing the Stack Trace

Microsoft provide a debugging library, DbgHelp.dll, which provides among other things functions to walk up the stack and print out the return addresses. A full description of DbgHelp.dll is outside the scope of this article - I refer you to Matt Peitrek's MSJ article [5] or John Robbins' book [6] if you want more details. 
The StackWalk() function provided by the DbgHelp DLL takes nine parameters, but the key ones are a StackFrame and a ContextRecord. The StackFrame is an in/out parameter used to contain data for successive stack frames and the ContextRecord contains the thread state in a platform dependent manner. (Different definitions of the structure are given in WinNt.h and the correct one is picked by the C++ preprocessor). The ContextRecord is technically optional, but contains enough information to initialise the StackFrame and also improve the reliability of the StackWalk function so it is preferable to require one.

So here is a function prototype for a simple stack trace routine implemented using the functionality of DbgHelp.dll:

	void SimpleSymbolEngine::StackTrace( CONTEXT *pContextRecord, std::ostream & os );
The implementation of this function sets up AddrPC, AddrFrame and AddrStack in a StackFrame record from the Eip, Ebp and Esp registers in the context record and then calls StackWalk repeatedly until the stack walk is completed. Each frame address is printed, together with the return address. Two functions in the DbgHelp library (SymGetSymFromAddr and SymGetLineFromAddr) are called to get any symbolic information available for the return address. Note that even if you don't have debug symbols for your program (and DLLs) DbgHelp will try to provide information based on any exported names from in DLLs.

A context record can be obtained in a variety of ways. As it is simply a snapshot of the thread state it could be built up manually using inline assembler to populate the various fields from CPU registers - or more easily by using the GetThreadContext call. The operating system also uses them in various places when managing thread state and finally they also crop up in exception handling.

The main reason to write the 'ExceptionHelper' class is to obtain the context record of the thread state when the exception occurred. We can then use this key piece of data to extract the stack trace. Let's look at Microsoft's implementation of try, throw and catch in Win32 C++ to see how it lets us build something to extract this information.

Structured exception handling

Microsoft integrated standard C++ exception handling with window's own exception handling model: so-called "structured exception handling" or "SEH" and this section tries to give an overview of what is happening inside SEH from an application's viewpoint - however you don't need to completely understand the principles to follow the ExceptionHelper code.

The definitive article about Win32 structured exception handling is by Matt Peitrek [2] and I refer interested readers there. However, his article focuses on the Microsoft extensions to support SEH: _try, _except and _finally and less on the language native concepts embodied in try, throw, etc. Other articles, such as [3], focus on what is happening at the assembler level which is great for the minority of programmers who understand assembler but not for the rest.

There is a relatively natural fit between the SEH exception model and the 'try ... catch' exception model in programming languages such as C++ so it is not too surprising that Microsoft decided to use this operating system level structured exception handling to provide the basis for their C++ exception handling code. However other implementors of C++ on the Win32 platform have not necessarily followed the same pattern.

Windows provides a portable exception architecture which recognises two main type of exceptions: 'system' exceptions such as an access violation or an integer divide by zero, which are also known as 'asynchronous' exceptions, and 'user' exceptions generated by a call to RaiseException(), which are also known as 'synchronous' exceptions. Each thread contains a linked list of exception handlers and when an exception occurs information about the exception and a context record for the thread are passed to each exception handler in the chain in turn for possible processing. There are several things each handler can do; the commonest cases are:

  • return 'keep looking' (and the next exception handler will be called)
  • unwind the thread context back to a known state and execute a failure path (in C++, a 'catch' block).
If the context is to be unwound then each exception handler which is unwound off the stack is called so it can perform any required tidy-up. If all of the exception handlers return 'keep looking' the operating system has a final, process wide, exception handler which by default produces one of the 'Application Error' popups. (Note that this is a slight simplification of the full picture)

Each handler has a signature like this:

DWORD exceptionHandler( EXCEPTION_RECORD *pException, EXCEPTION_REGISTRATION_RECORD *pRegistrationRecord, CONTEXT *pContext );
Where:
  • pException contains information about the exception being processed, such as the exception code and the fault address.
  • pRegistrationRecord points to the current node in the list of exception handlers
  • pContext contains the processor-specific thread state when the exception occurred
Our task is to retain the thread context from the last parameter so we can use it later in a call to the StackTrace function.

What makes this exception style 'structured' is that the chain of exception handlers exists in the thread's own stack. In a typically block-structured programming language each call to a function, method or procedure pushes a new activation frame onto the stack; this frame contains the current arguments, any local variables and the exception handler for this function (if any). Additionally, the algorithm which passes the exception along the chain of handlers naturally moves from the most recently called function up the stack to the top most function.

In the Win32 world the 'ESP' register contains the current stack pointer, by convention the current frame pointer is usually held in the 'EBP' register and the 'FS' selector register holds the base of the thread information block (TIB) which holds, among other things, the head of the exception chain.

To try and make this clearer here is a schematic representation of the bottom of the stack when function 'A' has called function 'B' which in turn has called function 'C'. Functions A and C have an SEH handler, but function B doesn't.

Inside the each stack frame the function arguments are above the frame register (and appear in assembler as [EBP + offset]) and local variables are below the frame register (and appear in assembler as [EBP - offset]). In practice things are more complicated than this - particularly when the optimiser gets involved - and the frame register EBP can get used for other purposes. To reduce the complexity of this article I'm not going to worry about optimised code.

We need insert our own exception helper object into the chain of exception registration records so we can extract the context record for the thrown exception.

MSVC exception handling

Before we can write our own exception handler we need to know a bit about how MSVC makes use of SEH handling to implement C++ exceptions. I've annotated the following code fragment with some of the key places that SEH handling is involved.
	void thrower()
        {
		SomeClass anObject; // ** 5 **
		throw std::runtime_error( "An error" ); // ** 2 **
	}

	void catcher()
	{	// ** 1 **
		std::string functionName( "catcher" );
		try
		{
			thrower();
		}
		catch ( std::exception & ex )	// ** 3 **
		{
			std::cerr << functionName << ": " << ex.what() << std::endl;	
		}
	}	// ** 4 **

1) When you write a function containing 'try' ... 'catch' the Microsoft compiler adds code to the function prolog to register a structured exception handler for this function at the head of the thread's exception handler chain. The actual structure created for the exception registration extends the basic EXCEPTION_REGISTRATION_RECORD; the additional fields are used for managing the exception handling state and recovering the stack pointer.

2) 'Throw' is implemented by calling RaiseException with a special exception code 0xe06d7363 (the low bits spell 'msc' in ascii). Other fields in the exception record are set up to hold the address and run-time type of the thrown object - in this case a std::runtime_error object.

3) The catch code is actually implemented by the exception handler. If the exception being handled has the special exception code value then the run-time type information is extracted and compared to the type of the argument in the catch statement. If a match is found (or a conversion is possible) then the exception chain is unwound and the body of the catch is entered, after which the execution will continue directly after the try ... catch block. If a match is not found the exception handler returns the 'keep looking' value and the new handler in the chain will be tried.

4) On function exit the exception handler for catcher is removed from the chain.

5) There's another place that SEH code is needed of course - the destructor for 'anObject' must be called during the unwind back to the 'catch' statement. So there is actually yet another exception handler registered for 'thrower' too, to deal with ensuring that anObject gets deleted. This one never tries to handle the exception but simply ensures local variables are destructed during the unwind.

One key thing about the way MSVC exception handling works is that it involves making extra calls down the stack. At point (2) the C++ runtime calls RaiseException, which snapshots the exception and thread state and then it in turn calls the code to work along the exception chain calling exception handlers. At point (3) when the exception handler for 'catcher' gets control it is a long way down the stack. The exception chain is unwound by yet another call, this time to RtlUnwind. This function throws another exception along the exception chain with a special flag value 'EXCEPTION_UNWINDING' set, giving each exception handler in turn a chance to do tidying up before it is removed from the exception chain. After returning from RtlUnwind the body of the catch statement is then called. When the catch body completes control returns back to the C++ runtime which completes tidying up the stack pointer, deletes the exception object and then resumes execution at the next instruction after the catch block.

So how does the catch block make use of the local variable 'functionName' if it is so far down the stack when it gets control?

What the C++ runtime does is to use the extended exception registration record (passed to the handler as the second argument) to recover the value of the frame pointer EBP. Having reset the frame pointer the code in the catch body can make use of local variables and function arguments without difficulty. It is does not affect the function that the stack pointer is not simply a few bytes below the frame pointer but several hundred bytes below it.

The upshot is that, when the catch body is executed, the complete stack down to the location of the 'throw' is still available in memory. The raw stack pointer will only be reset when the body of the stack completes, and before this point the call stack will not be touched. So if we can obtain the address of the context record that was passed into each exception handler as the third argument, the pointer will still be valid inside the body of the catch.

Looking back to the way the chain of exception handlers is processed we can see that if we can hook our code into the exception chain just before the compiler written exception handler we can extract information from the context record and then use that information inside the catch handler to allow us print a stack trace. Let's look at how we can do this.

Adding to the exception chain

The exception chain in Win32 consists of a singly linked list of EXCEPTION_REGISTRATION_RECORDs on the stack. Unfortunately Microsoft do not provide a C++ definition for this structure (possibly because it is different on each hardware platform running Windows) but they do provide one in an assembler include file EXSUP.INC which can be translated into C++ like this:
	/** Typedef for the exception handler function prototype */
	typedef DWORD (fnExceptionHandler)( EXCEPTION_RECORD *pException, struct _EXCEPTION_REGISTRATION_RECORD *pRegistrationRecord, CONTEXT *pContext );

	/** Definition of 'raw' WinNt exception registration record - this ought to be in WinNt.h */
	struct _EXCEPTION_REGISTRATION_RECORD
	{
	    struct _EXCEPTION_REGISTRATION_RECORD *PrevExceptionRegistrationRecord; // Chain to previous record
	    fnExceptionHandler *ExceptionHandler; // Handler function being registered
	};
So all we need to do, it seems, is to create an _EXCEPTION_REGISTRATION_RECORD, point ExceptionHandler to our exception handling function and insert the record at the top of the exception chain. Almost. There are a some complexities with registering your own exception handlers.

Firstly, the code which walks the exception chain requires (on some but not all versions of Windows) that the nodes in the chain are registered in strict address order. Fortunately the compiler always puts local variables below the exception registration record so by using a local variable for our exception helper we should always be able to insert it into the chain before the compiler generated exception registration record.

Secondly, I want to register the exception handler in a constructor. This function too has a compiler-generated exception handler. I must ensure that the handler is registered in the chain above the record for the constructor or my exception handler will be unregistered when the constructor completes! Additionally I want the code to work properly should there be two or more exception helper objects in a single function, and it is not in general possible to fix the offsets in the stack frame assigned by the compiler for local variables.

Lastly, as part of the security improvements included with Visual Studio .NET 2003 and Windows Server 2003/Windows XP service pack 2, the exception handling function to be called must be marked with a special attribute ('SAFESEH') at link time so it will appear in the "Safe Exception Handler Table" in the load configuration record. Failure to do this results in a security exception occurring at runtime which usually terminates the process. This check has been added to Windows to prevent security exploits that use buffer overrun in order to replace the exception handler address on the stack with a pointer to injected code. The SAFESEH attribute can only be granted by assembler code so it is therefore necessary, when using Visual Studio .NET 2003, to make use of a very simple piece of assembler code to add this attribute to the exception handling function.

Note: the assembler 'ml.exe' provided with the first Beta edition of Visual Studio 2005 access violates when using /safeseh [4] and that from 2003 must be used.

One mechanism I've found that provides good ease of use under the above constraints is to create a common base class for my own exception handlers. This class contains a static exception handling function which can be marked, once and for all, with the SAFESEH attribute. This common handler then makes a virtual call into the derived class for the specific action required for exception handling.

	class ExceptionHelperBase : public _EXCEPTION_REGISTRATION_RECORD
	{
	public:
	    /** Construct helper object */
	    ExceptionHelperBase();

	    /** Make safe to extend */
	    virtual ~ExceptionHelperBase() {}

	    /** Allow subclass to hook exception */
	    virtual void onException( EXCEPTION_RECORD *pException, CONTEXT *pContext ) = 0;

	private:
	    // Disable copy and assign
	    ExceptionHelperBase( ExceptionHelperBase const & );
	    ExceptionHelperBase& operator=( ExceptionHelperBase const & );

            // The one and only exception handler function
	    static fnExceptionHandler exceptionHandler;
	};
The exception handling function simply casts the exception registration record back to an ExceptionHelperBase and invokes 'onException':
	DWORD ExceptionHelperBase::exceptionHandler( EXCEPTION_RECORD *pException, struct _EXCEPTION_REGISTRATION_RECORD *pRegistrationRecord, CONTEXT *pContext )
	{
	    ExceptionHelperBase &self = static_cast( *pRegistrationRecord );
	    self.onException( pException, pContext );
            return ExceptionContinueSearch;
	}
I would like my exception class to register itself in the constructor and deregister itself in the destructor. Unfortunately I can't simply do this in the ExceptionHelperBase constructor and destructor without risking problems if I get an exception during the constructor/destructor code itself. However, a use of the 'curiously recurring template pattern' fixes this problem and ensures registration happens last in the constructor and first in the destructor:
	    template 
	    class AutoRegister : public RegistrationRecord
	    {
	    public:
	        /** Auto-register an exception record for 'RegistrationRecord' */
	        AutoRegister()
	        : RegistrationRecord()
	        {
	            registerHandler( this );
	        }

	        /** Unregister and destroy an exception record */
	        ~AutoRegister()
	        {
	            unregisterHandler( this );
	        }
	    };
Where 'registerHandler' will install the handler in the exception chain and 'unregisterHandler' will remove it from the chain by using standard logic for singly-linked lists. The list head is held in the NT_TIB structure pointed to by the FS register and the list tail is the value "-1".

Processing the exception

The first derived class simply prints out the exception information to demonstrate that things are working properly:
	class ExceptionHelperImpl1 : public ExceptionHelperBase
	{
	    /** Print the address of the exception records */
	    virtual void onException( EXCEPTION_RECORD *pException, CONTEXT *pContext )
	    {
	       printf( "pException: %p (code: %p, flags: %x), pContext: %p\n", pException, pException->ExceptionCode, pException->ExceptionFlags, pContext );
	    }
	};

	typedef AutoRegister< ExceptionHelperImpl1 > ExceptionHelper1;
Since this code is executing while an exception is actually being processed I used printf() rather than std::cout to avoid any potentially harmful interactions with the standard library.

Sample code:

	int main()
	{
	    ExceptionHelper1 helper;

	    try
	    {
	        printf( "About to throw\n" );
	        throw std::runtime_error( "basic exception" );
	    }
	    catch ( std::exception & /*ex*/ )
	    {
	        printf( "In catch handler\n" );
	    }
            return 0;
	}
when executed this program generates output for two exceptions:
	About to throw
	pException: 0012FBA0 (code: E06D7363, flags: 1), pContext: 0012FBC0
	pException: 0012FBA0 (code: E06D7363, flags: 3), pContext: 0012F670
	In catch handler
The second call is generated by the Microsoft supplied exception handler unwinding the exception chain. This is easily identified as the EXCEPTION_UNWINDING flag (value 2) is set in pException->ExceptionFlags for the second exception. For our purposes we want to extract context data from the first call since this context describes the thread state when 'throw' was executed. (Note that our exception handler is removed from the chain during the exception unwind so would need to be re-inserted to catch subsequent exceptions in the same scope)

We now have everything we need for the basic version of the code to print a stack trace:

	class ExceptionStackTraceImpl : public ExceptionHelperBase
	{
	public:
	    ExceptionStackTraceImpl() : pSavedContext(0) {}

	    /** Use the saved pointer to print the stack trace */
	    void printStackTrace( std::ostream & os ) const
	    {
	        if ( pSavedContext != 0 )
	            SimpleSymbolEngine::instance().StackTrace( pSavedContext, os );
	    }

	private:
	    /** Capture the thread context when the initial exception occurred */
	    virtual void onException( EXCEPTION_RECORD *pException, CONTEXT *pContext )
	    {
	       if ( ( pException->ExceptionFlags & EXCEPTION_UNWINDING ) == 0 )
	       {
	           pSavedContext = pContext;
	       }
	    }

	    PCONTEXT pSavedContext; // context record from the last exception
	};

	typedef AutoRegister< ExceptionStackTraceImpl > ExceptionStackTrace;
We have now achieved the original aim of be able to print a stack trace in the catch block.

Interaction with normal SEH

This method of 'hooking' in to the MSVC handling of C++ exceptions means that the exception handler is also called for every other SEH exception, such as access violation. In released versions of MSVC the implementation of catch (...) also processed these types of exceptions. Although this seems at first sight to be a good thing it actually tends to cause more problems than it solves. One particular issue is that genuine problems such as corrupt memory, I/O errors on the paging file or load time problems with DLLs get handled in the same way as a C++ exception of unknown type, by code not written to deal with these error conditions. For current versions of MSVC it is usually best to avoid use of catch( ... ) unless either the code re-throws the exception or terminates the process.

Visual Studio 2005 Beta 1 handles non-C++ SEH exceptions in a catch( ... ) only when the compiler flag /EHa is set, which is a great improvement and gives maximum flexibility.

Whether or not /EHa is specified we can use ExceptionHelper to extract information about the OS exception. Microsoft provide some other ways to achieve a similar end, __try/__except and _set_se_translator, but they are not total solutions. Also not all compler vendors provide such extensions and the ExceptionHelper code could still be used to extract information about the exception.

For example I modified ExceptionHelper1 class for gcc on win32 (a couple of minor changes were required for a clean compile). Since gcc does not seem to use SEH for C++ exception handling ExceptionHelper1 did not capture information for such exceptions but it did do so for Win32 exceptions, such as access violations. For a "proof of concept" I changed the exception handler to throw a std::runtime exception rather than printing the exception information and was successful in mapping an SEH exception into a C++ exception, thus allowing additional tidyup to be performed before the program exited.

Conclusion

I have given a brief overview of how the Microsoft compilers on Win32 implement C++ exception handling. Using this information we've seen a simple class which enables additional information to be obtained about the exception during program execution.

What is the main strength and weakness of this approach?

On the positive side it enables better diagnostic information to be produced at runtime for MSVC on win32. This can significantly reduce the cost of finding bugs, since enough information might be gathered in the field to identify the root cause. Without this extra information it might be necessary to try and reproduce the problem under a debugger, with potential difficultly of getting the right execution environment to enure the problem does in fact appear.

The main weakness is that the code is platform specific and relies on undocumented behaviour of the compiler. Other compilers under Win32 do not use the SEH mechanism to handle C++ exceptions so this code is useless should your code need to be portable to them. The implementation of the exception mechanism even under 64bit windows is not the same as for 32 bit windows, so the technique described here will not work unchanged (if at all) in that environment even for Microsoft compilers.

The decision depends on the relative balance between these two items. However, having isolated the logic into a single class 'ExceptionStackTrace' it can be conditionally compiled to a do-nothing implementation on other platforms, or if may even be possible to re-implement the logic for another platform.

Updates

A couple of readers have pointed out that with VS 2005/2008 the assembler file needs SYSCALL added to the PROTO directive. VS 2003 works with or without this.

I have made this change to the code in the zip file.

Klaus Triendl also pointed out that the exception handler should have a 4th parameter `void* DispatcherContext'. This is correct, but the final parameter can be safely ignored if it is not used, as the function uses the C calling convention and so the caller is responsible for clearing up the stack.

Note on multi-threaded programs.

The simple stack walking code above may fail in unpredictable ways if an exception occurs in more than one thread at once. The Microsoft documentation for the functions in the DbgHelp library states: 
"All DbgHelp functions, such as this one, are single threaded. Therefore, calls from more than one thread to this function will likely result in unexpected behavior or memory corruption. To avoid this, you must synchronize all concurrent calls from more than one thread to this function."

Hence if you wish to debug multi-threaded programs you will need to add some explicit synchronisation to the access to the DbgHelp library. My apologies to Jonathan Lepolt, who was affected by this problem, for not making this clear in the original article.

References

[1] 'Efficient Exceptions?', Overload 61, June 2004
[2] 'A Crash Course on the Depths of Win32? Structured Exception Handling', Matt Pietrek, http://www.microsoft.com/msj/0197/Exception/Exception.aspx
[3] 'Win32 Exception handling for assembler programmers', Jeremy Gordon, http://www.jorgon.freeserve.co.uk/Except/Except.htm
[4] 'MSDN Product Feedback Center', http://lab.msdn.microsoft.com/ProductFeedback, search on keyword "FDBK12741"
[5] 'Improved Error Reporting with DBGHELP 5.1 APIs', Matt Pietrek, http://msdn.microsoft.com/msdnmag/issues/02/03/hood/default.aspx
[6] 'Debugging Applications for Microsoft .NET and Microsoft Windows', John Robbins, Microsoft Press
Source code for the article
Copyright (c) Roger Orr - rogero@howzatt.demon.co.uk 
Published in Overload issue 63, October 2004.
$Revision: 1.4 $ $Date: 2008/09/24 22:33:18 $

posted on 2012-02-06 00:23 楊粼波 閱讀(1614) 評論(0)  編輯 收藏 引用 所屬分類: C++

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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看| 一本大道久久精品懂色aⅴ| 欧美日韩日本视频| 久久九九全国免费精品观看| 亚洲欧美中文日韩在线| 亚洲国产精品电影在线观看| 久久天堂av综合合色| 欧美精品成人一区二区在线观看 | 猛干欧美女孩| 亚洲人成亚洲人成在线观看| 日韩亚洲精品在线| 午夜日韩福利| 久久男女视频| 亚洲一区二区三区777| 国产精品欧美日韩一区| 久久精品中文| 亚洲欧美日本国产有色| 欧美xart系列高清| 午夜精品一区二区三区在线视| 亚洲第一精品福利| 国产美女一区二区| 欧美日韩一区二区欧美激情| 久久久久国产精品一区| 日韩香蕉视频| 日韩午夜av| 久久不见久久见免费视频1| 欧美成人免费全部| 欧美在线视频二区| 亚洲桃花岛网站| 亚洲精选中文字幕| 国模套图日韩精品一区二区| 欧美揉bbbbb揉bbbbb| 欧美精品大片| 欧美日产一区二区三区在线观看| 欧美主播一区二区三区美女 久久精品人 | 欧美一级视频一区二区| 亚洲图片欧美午夜| 夜久久久久久| 亚洲免费一在线| 9l视频自拍蝌蚪9l视频成人| 最近看过的日韩成人| 亚洲第一精品夜夜躁人人爽| 在线看视频不卡| 亚洲美女福利视频网站| 夜夜夜久久久| 午夜亚洲福利| 老司机免费视频一区二区三区| 久久中文久久字幕| 欧美激情在线有限公司| 亚洲国产日韩欧美综合久久| 亚洲伦理久久| 久久岛国电影| 欧美日韩精品免费| 国内精品久久久久影院 日本资源| 国产一区二区三区无遮挡| 亚洲国产免费| 香港久久久电影| 久久综合五月| 亚洲精品欧美日韩| 欧美影院成人| 欧美黄色一级视频| 国产综合精品| 一区二区三区视频在线观看| 欧美另类久久久品| 国产在线精品二区| 一区二区三区|亚洲午夜| 久久99伊人| 日韩一级免费| 免费日韩成人| 国内精品国语自产拍在线观看| 亚洲伦理在线观看| 午夜视频一区在线观看| 亚洲国产导航| 久久久久在线观看| 亚洲综合欧美日韩| 国产精品久久久一区麻豆最新章节 | 欧美电影在线免费观看网站| 欧美国产成人精品| 欧美一区二区三区的| 日韩系列欧美系列| 一本色道久久99精品综合| 久久精品日韩| 亚洲欧美日本视频在线观看| 欧美日本在线| 在线视频免费在线观看一区二区| 欧美粗暴jizz性欧美20| 久久精品国产精品亚洲精品| 国产精品久久久久一区二区三区 | 亚洲国产精品精华液2区45| 亚洲欧美在线看| 亚洲婷婷国产精品电影人久久| 欧美激情偷拍| 在线亚洲精品| 午夜精品久久久久久久男人的天堂| 欧美亚州韩日在线看免费版国语版| 亚洲图片你懂的| 中文国产一区| 狠狠色丁香婷婷综合久久片| 欧美高清视频一二三区| 欧美日韩成人在线| 久久gogo国模裸体人体| 久久久999精品免费| 久久亚洲春色中文字幕| 亚洲午夜在线| 美日韩精品免费| 亚洲欧美日韩在线观看a三区 | 国产精品久99| 欧美sm视频| 国产精品综合视频| 国产女主播视频一区二区| 欧美成人一区二区三区在线观看| 欧美连裤袜在线视频| 久久久久久9| 国产精品久久久久久久电影| 久久亚洲欧美国产精品乐播| 欧美日韩一区二区三| 欧美xx69| 国色天香一区二区| 亚洲图片你懂的| 在线一区二区三区四区五区| 久久精品国产第一区二区三区| 亚洲欧美日本国产专区一区| 欧美成人久久| 欧美激情亚洲一区| 亚洲第一在线视频| 久久综合图片| 欧美激情一区二区久久久| 激情成人中文字幕| 久久综合福利| 亚洲二区免费| 亚洲黄色在线| 欧美欧美全黄| 一区二区三区高清不卡| 精品粉嫩aⅴ一区二区三区四区| 亚洲专区一区| 噜噜噜91成人网| 亚洲成在人线av| 欧美成人一二三| 亚洲最新视频在线| 久久黄色网页| 亚洲精品一区二区三区四区高清| 久久精品一区蜜桃臀影院| 国产欧美在线| 欧美大片免费看| 亚洲天堂成人在线视频| 久久精品综合网| 亚洲国产日韩综合一区| 欧美国产日韩一区二区在线观看| 在线精品亚洲| 国产精品久久久久久久午夜片| 欧美一级淫片播放口| 国产亚洲欧美日韩在线一区| 久久欧美肥婆一二区| 正在播放欧美视频| 免费影视亚洲| 小处雏高清一区二区三区| 亚洲国产激情| 国产一区二区三区高清播放| 欧美一区二区三区免费视| 亚洲黄色一区| 欧美激情麻豆| 欧美激情第8页| 免费国产一区二区| 久久蜜桃资源一区二区老牛| 亚洲淫性视频| 在线视频欧美一区| 亚洲精品网址在线观看| 久久久久网址| 老司机精品视频网站| 欧美在线一级视频| 先锋影院在线亚洲| 一本一本a久久| 一本综合久久| 亚洲视频1区| 亚洲一区二区免费| 亚洲视频久久| 亚洲欧美另类久久久精品2019| av成人黄色| 亚洲夜晚福利在线观看| 亚洲午夜视频在线| 亚洲欧美日韩电影| 久久成人18免费观看| 久久综合九色综合欧美狠狠| 久热综合在线亚洲精品| 美女免费视频一区| 国产午夜一区二区三区| 精品成人在线| 一区二区三区日韩精品| 亚洲欧美激情视频| 久久精品亚洲精品| 欧美国产一区二区在线观看| 亚洲精品影院在线观看| 亚洲视频高清| 久久精品一区中文字幕|