1/*
2 * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25#include "awt.h"
26#include "awt_Toolkit.h"
27#include "debug_mem.h"
28
29extern void DumpJavaStack();
30
31#if defined(DEBUG)
32
33////////////////////////////////////////////////////////////////////////////////////
34// avoid pulling in our redefinition of 'new'
35// since we actually need to implement it here
36#if defined(new)
37#undef new
38#endif
39//
40
41void * operator new(size_t size, const char * filename, int linenumber) {
42    void * ptr = DMem_AllocateBlock(size, filename, linenumber);
43    if (ptr == NULL) {
44        throw std::bad_alloc();
45    }
46
47    return ptr;
48}
49
50void * operator new[](size_t size, const char * filename, int linenumber) {
51    void * ptr = DMem_AllocateBlock(size, filename, linenumber);
52    if (ptr == NULL) {
53        throw std::bad_alloc();
54    }
55
56    return ptr;
57}
58
59#if _MSC_VER >= 1200
60void operator delete(void *ptr, const char*, int) {
61    DASSERTMSG(FALSE, "This version of 'delete' should never get called!!!");
62}
63#endif
64void operator delete(void *ptr) throw() {
65    DMem_FreeBlock(ptr);
66}
67
68////////////////////////////////////////////////////////////////////////////////////
69
70static void DumpRegion(HRGN rgn) {
71    DWORD size = ::GetRegionData(rgn, 0, NULL);
72    char* buffer = (char *)safe_Malloc(size);
73    memset(buffer, 0, size);
74    LPRGNDATA rgndata = (LPRGNDATA)buffer;
75    rgndata->rdh.dwSize = sizeof(RGNDATAHEADER);
76    rgndata->rdh.iType = RDH_RECTANGLES;
77    VERIFY(::GetRegionData(rgn, size, rgndata));
78
79    RECT* r = (RECT*)(buffer + rgndata->rdh.dwSize);
80    for (DWORD i=0; i<rgndata->rdh.nCount; i++) {
81        if ( !::IsRectEmpty(r) ) {
82            DTrace_PrintImpl("\trect %d %d %d %d\n", r->left, r->top, r->right, r->bottom);
83        }
84        r++;
85    }
86
87    free(buffer);
88}
89
90/*
91 * DTRACE print callback to dump HDC clip region bounding rectangle
92 */
93void DumpClipRectangle(const char * file, int line, int argc, const char * fmt, va_list arglist) {
94    const char *msg = va_arg(arglist, const char *);
95    HDC         hdc = va_arg(arglist, HDC);
96    RECT        r;
97
98    DASSERT(argc == 2 && hdc != NULL);
99    DASSERT(msg != NULL);
100
101    ::GetClipBox(hdc, &r);
102    DTrace_PrintImpl("%s: hdc=%x, %d %d %d %d\n", msg, hdc, r.left, r.top, r.right, r.bottom);
103}
104
105/*
106 * DTRACE print callback to dump window's update region bounding rectangle
107 */
108void DumpUpdateRectangle(const char * file, int line, int argc, const char * fmt, va_list arglist) {
109    const char *msg = va_arg(arglist, const char *);
110    HWND        hwnd = va_arg(arglist, HWND);
111    RECT        r;
112
113    DASSERT(argc == 2 && ::IsWindow(hwnd));
114    DASSERT(msg != NULL);
115
116    ::GetUpdateRect(hwnd, &r, FALSE);
117    HRGN rgn = ::CreateRectRgn(0,0,1,1);
118    int updated = ::GetUpdateRgn(hwnd, rgn, FALSE);
119    DTrace_PrintImpl("%s: hwnd=%x, %d %d %d %d\n", msg, hwnd, r.left, r.top, r.right, r.bottom);
120    DumpRegion(rgn);
121    DeleteObject(rgn);
122}
123
124//
125// Declare a static object to init/fini the debug code
126//
127// specify that this static object will get constructed before
128// any other static objects (except CRT objects) so the debug
129// code can be used anywhere during the lifetime of the AWT dll
130#pragma warning( disable:4073 ) // disable warning about using init_seg(lib) in non-3rd party library code
131#pragma init_seg( lib )
132
133static volatile AwtDebugSupport DebugSupport;
134static int report_leaks = 0;
135
136AwtDebugSupport::AwtDebugSupport() {
137    DMem_Initialize();
138    DTrace_Initialize();
139    DAssert_SetCallback(AssertCallback);
140}
141
142AwtDebugSupport::~AwtDebugSupport() {
143    if (report_leaks) {
144        DMem_ReportLeaks();
145    }
146    DMem_Shutdown();
147    DTrace_Shutdown();
148}
149
150static jboolean isHeadless() {
151    jmethodID headlessFn;
152    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
153    jclass graphicsEnvClass = env->FindClass(
154        "java/awt/GraphicsEnvironment");
155
156    if (graphicsEnvClass != NULL) {
157        headlessFn = env->GetStaticMethodID(
158            graphicsEnvClass, "isHeadless", "()Z");
159        if (headlessFn != NULL) {
160            return env->CallStaticBooleanMethod(graphicsEnvClass,
161                                                headlessFn);
162        }
163    }
164    return true;
165}
166
167
168void AwtDebugSupport::AssertCallback(const char * expr, const char * file, int line) {
169    static const int ASSERT_MSG_SIZE = 1024;
170    static const char * AssertFmt =
171            "%s\r\n"
172            "File '%s', at line %d\r\n"
173            "GetLastError() is %x : %s\r\n"
174            "Do you want to break into the debugger?";
175
176    static char assertMsg[ASSERT_MSG_SIZE+1];
177    DWORD   lastError = GetLastError();
178    LPSTR       msgBuffer = NULL;
179    int     ret = IDNO;
180    static jboolean headless = isHeadless();
181
182    FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
183                  FORMAT_MESSAGE_FROM_SYSTEM |
184                  FORMAT_MESSAGE_IGNORE_INSERTS,
185                  NULL,
186                  lastError,
187                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
188                  (LPSTR)&msgBuffer, // it's an output parameter when allocate buffer is used
189                  0,
190                  NULL);
191
192    if (msgBuffer == NULL) {
193        msgBuffer = "<Could not get GetLastError() message text>";
194    }
195    // format the assertion message
196    _snprintf(assertMsg, ASSERT_MSG_SIZE, AssertFmt, expr, file, line, lastError, msgBuffer);
197    LocalFree(msgBuffer);
198
199    // tell the user the bad news
200    fprintf(stderr, "*********************\n");
201    fprintf(stderr, "AWT Assertion Failure\n");
202    fprintf(stderr, "*********************\n");
203    fprintf(stderr, "%s\n", assertMsg);
204    fprintf(stderr, "*********************\n");
205
206    if (!headless) {
207        ret = MessageBoxA(NULL, assertMsg, "AWT Assertion Failure",
208                          MB_YESNO|MB_ICONSTOP|MB_TASKMODAL);
209    }
210
211    // if clicked Yes, break into the debugger
212    if ( ret == IDYES ) {
213        # if defined(_M_IX86)
214            _asm { int 3 };
215        # else
216            DebugBreak();
217        # endif
218    }
219    // otherwise, try to continue execution
220}
221
222void AwtDebugSupport::GenerateLeaksReport() {
223    report_leaks = 1;
224}
225
226#endif // DEBUG
227