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

牽著老婆滿街逛

嚴以律己,寬以待人. 三思而后行.
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>
            亚洲大片免费看| 欧美视频在线一区二区三区| 久久国产乱子精品免费女 | 亚洲国产91精品在线观看| 国产深夜精品福利| 国内精品久久久久久久果冻传媒| 韩国女主播一区二区三区| 一区二区三区在线视频播放| 一区二区在线不卡| 一区二区电影免费在线观看| 亚洲线精品一区二区三区八戒| 新67194成人永久网站| 亚洲欧洲综合另类| 亚洲欧美在线x视频| 免费不卡在线观看| 亚洲精品精选| 日韩视频永久免费| 欧美在线一区二区| 欧美啪啪一区| 国产午夜精品一区二区三区视频| 国模套图日韩精品一区二区| 在线看国产一区| 国产精品99久久久久久久久久久久| 国产伦精品一区| 亚洲承认在线| 久久精品成人| 亚洲精选在线观看| 久久精品在线播放| 国产精品你懂的在线| 在线日韩av| 亚洲砖区区免费| 久久亚洲精品一区二区| 香蕉av福利精品导航| 欧美国产欧美亚洲国产日韩mv天天看完整 | 国产精品亚洲不卡a| 伊人久久亚洲美女图片| 欧美国产欧美亚洲国产日韩mv天天看完整 | 国产精品久久久久久久久久免费| 国产视频自拍一区| 99国产精品视频免费观看| 欧美在线视频免费播放| 亚洲精品美女| 久热精品视频在线观看| 国产精品嫩草99av在线| 亚洲人成在线播放| 噜噜噜躁狠狠躁狠狠精品视频| 91久久国产精品91久久性色| 欧美一区二区三区播放老司机| 亚洲国产一区在线观看| 久久久99精品免费观看不卡| 国产精品久久久久久久久久三级| 99re热精品| 亚洲欧洲一区二区在线观看 | 久久久精品一区| 国产精品亚洲视频| 亚洲欧美国产视频| 亚洲免费大片| 欧美三级视频在线观看| 亚洲精品永久免费精品| 免费的成人av| 久热精品视频在线免费观看| 国产亚洲一区二区三区| 欧美中文在线视频| 欧美一区二区三区另类| 国产亚洲欧洲997久久综合| 亚洲女性喷水在线观看一区| 日韩午夜在线电影| 亚洲乱码国产乱码精品精98午夜| 免费在线看成人av| 亚洲黄色尤物视频| 欧美成人亚洲| 欧美精品网站| 亚洲永久网站| 免费人成精品欧美精品| 久久久久成人精品免费播放动漫| 一区二区在线免费观看| 亚洲国产精品黑人久久久| 久久不射2019中文字幕| 在线免费高清一区二区三区| 久久综合网hezyo| 亚洲精品看片| 亚洲天天影视| 黄色在线成人| 亚洲国内自拍| 国产精品伦理| 久久一区中文字幕| 欧美精品免费在线观看| 午夜日韩在线| 久久蜜臀精品av| 在线中文字幕日韩| 亚洲欧美日韩国产| 久久久水蜜桃av免费网站| 亚洲免费观看高清在线观看| 亚洲免费观看视频| 国产色产综合产在线视频| 欧美xx视频| 国产精品久久久久久久久婷婷| 久久美女性网| 国产精品jizz在线观看美国| 欧美午夜电影一区| 国产精品免费看片| 欧美激情亚洲国产| 国产精品三级视频| 亚洲国产激情| 国产午夜精品在线| 日韩一级黄色av| 亚洲国产裸拍裸体视频在线观看乱了 | 欧美一区二区福利在线| 久久一区中文字幕| 香蕉久久夜色精品国产使用方法 | 欧美一区三区三区高中清蜜桃 | 久久在线免费视频| 国产精品久久久一区二区三区| 麻豆精品视频| 国产美女扒开尿口久久久| 亚洲黄色毛片| 精品成人在线视频| 欧美午夜精品伦理| 久久久久看片| 国产无一区二区| 亚洲午夜极品| 伊伊综合在线| 欧美一级视频一区二区| 一区二区三区视频观看| 女同性一区二区三区人了人一| 欧美一区二区三区四区在线观看| 欧美日韩大片| 91久久国产综合久久| 狠狠久久婷婷| 久久se精品一区精品二区| 欧美主播一区二区三区| 亚洲国产成人精品久久| 欧美日韩高清在线一区| 亚洲欧洲另类国产综合| 欧美一区二区三区在线看| 欧美一级淫片aaaaaaa视频| 欧美日韩亚洲一区二区三区在线| 快射av在线播放一区| 国产一区二区三区的电影 | 99re在线精品| 亚洲美女黄色片| 欧美精品三级日韩久久| 亚洲毛片av| 亚洲色图制服丝袜| 欧美亚州一区二区三区| 在线亚洲欧美视频| 亚洲欧美日韩专区| 国产欧美日韩不卡免费| 午夜在线一区| 老妇喷水一区二区三区| 影音先锋久久久| 欧美高清自拍一区| 日韩一级免费| 欧美性久久久| 午夜影院日韩| 欧美 日韩 国产精品免费观看| 在线播放不卡| 欧美激情精品久久久久久| 亚洲精品视频在线播放| 亚洲桃花岛网站| 国产精品日韩在线一区| 欧美一区二区视频在线| 欧美大胆成人| 亚洲综合电影| 一区在线播放| 欧美精品免费在线观看| 亚洲亚洲精品三区日韩精品在线视频| 亚洲欧美电影院| 一区二区三区在线高清| 欧美日韩www| 午夜精品久久久久久久男人的天堂| 久久久国产午夜精品| 亚洲国产高清高潮精品美女| 欧美精品在线观看播放| 国产欧美 在线欧美| 亚洲欧美日韩一区| 在线播放国产一区中文字幕剧情欧美| 美女精品自拍一二三四| 一区二区三区视频在线 | 国产精品一区久久久| 欧美专区在线观看| 亚洲精品自在久久| 久久综合影视| 午夜电影亚洲| 亚洲国产经典视频| 国产精品一区视频网站| 男女视频一区二区| 亚洲欧美日韩精品综合在线观看| 欧美人与禽性xxxxx杂性| 美脚丝袜一区二区三区在线观看| 一区二区三区免费看| 在线观看不卡| 国产色爱av资源综合区| 欧美丝袜一区二区| 国产精品嫩草99a| 久久岛国电影| 亚洲每日更新| 亚洲国产日韩在线一区模特| 久久岛国电影| 亚洲一区3d动漫同人无遮挡|