/* * tkMacOSXDebug.c -- * * Implementation of Macintosh specific functions for debugging MacOS events, * regions, etc... * * Copyright 2001, Apple Computer, Inc. * Copyright (c) 2006-2007 Daniel A. Steffen * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * * The following terms apply to all files originating from Apple * Computer, Inc. ("Apple") and associated with the software * unless explicitly disclaimed in individual files. * * * Apple hereby grants permission to use, copy, modify, * distribute, and license this software and its documentation * for any purpose, provided that existing copyright notices are * retained in all copies and that this notice is included * verbatim in any distributions. No written agreement, license, * or royalty fee is required for any of the authorized * uses. Modifications to this software may be copyrighted by * their authors and need not follow the licensing terms * described here, provided that the new terms are clearly * indicated on the first page of each file where they apply. * * * IN NO EVENT SHALL APPLE, THE AUTHORS OR DISTRIBUTORS OF THE * SOFTWARE BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, * INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF * THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, * EVEN IF APPLE OR THE AUTHORS HAVE BEEN ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. APPLE, THE AUTHORS AND * DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS * SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND APPLE,THE * AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * GOVERNMENT USE: If you are acquiring this software on behalf * of the U.S. government, the Government shall have only * "Restricted Rights" in the software and related documentation * as defined in the Federal Acquisition Regulations (FARs) in * Clause 52.227.19 (c) (2). If you are acquiring the software * on behalf of the Department of Defense, the software shall be * classified as "Commercial Computer Software" and the * Government shall have only "Restricted Rights" as defined in * Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the * foregoing, the authors grant the U.S. Government and others * acting in its behalf permission to use and distribute the * software in accordance with the terms specified in this * license. * * RCS: @(#) $Id: tkMacOSXDebug.c,v 1.19.2.1 2008/06/19 00:15:04 das Exp $ */ #include "tkMacOSXPrivate.h" #include "tkMacOSXDebug.h" #ifdef TK_MAC_DEBUG typedef struct { EventKind kind; const char * name; } MyEventName; typedef struct { EventClass c; MyEventName * names; } MyEventNameList; static MyEventName windowEventNames [] = { { kEventWindowUpdate,"Update"}, { kEventWindowDrawContent,"DrawContent"}, { kEventWindowActivated,"Activated"}, { kEventWindowDeactivated,"Deactivated"}, { kEventWindowGetClickActivation,"GetClickActivation"}, { kEventWindowShowing,"Showing"}, { kEventWindowHiding,"Hiding"}, { kEventWindowShown,"Shown"}, { kEventWindowHidden,"Hidden"}, { kEventWindowBoundsChanging,"BoundsChanging"}, { kEventWindowBoundsChanged,"BoundsChanged"}, { kEventWindowResizeStarted,"ResizeStarted"}, { kEventWindowResizeCompleted,"ResizeCompleted"}, { kEventWindowDragStarted,"DragStarted"}, { kEventWindowDragCompleted,"DragCompleted"}, { kEventWindowClickDragRgn,"ClickDragRgn"}, { kEventWindowClickResizeRgn,"ClickResizeRgn"}, { kEventWindowClickCollapseRgn,"ClickCollapseRgn"}, { kEventWindowClickCloseRgn,"ClickCloseRgn"}, { kEventWindowClickZoomRgn,"ClickZoomRgn"}, { kEventWindowClickContentRgn,"ClickContentRgn"}, { kEventWindowClickProxyIconRgn,"ClickProxyIconRgn"}, { kEventWindowCursorChange,"CursorChange" }, { kEventWindowCollapse,"Collapse"}, { kEventWindowCollapsed,"Collapsed"}, { kEventWindowCollapseAll,"CollapseAll"}, { kEventWindowExpand,"Expand"}, { kEventWindowExpanded,"Expanded"}, { kEventWindowExpandAll,"ExpandAll"}, { kEventWindowCollapse,"Collapse"}, { kEventWindowClose,"Close"}, { kEventWindowClosed,"Closed"}, { kEventWindowCloseAll,"CloseAll"}, { kEventWindowZoom,"Zoom"}, { kEventWindowZoomed,"Zoomed"}, { kEventWindowZoomAll,"ZoomAll"}, { kEventWindowContextualMenuSelect,"ContextualMenuSelect"}, { kEventWindowPathSelect,"PathSelect"}, { kEventWindowGetIdealSize,"GetIdealSize"}, { kEventWindowGetMinimumSize,"GetMinimumSize"}, { kEventWindowGetMaximumSize,"GetMaximumSize"}, { kEventWindowConstrain,"Constrain"}, { kEventWindowHandleContentClick,"HandleContentClick"}, { kEventWindowProxyBeginDrag,"ProxyBeginDra}"}, { kEventWindowProxyEndDrag,"ProxyEndDrag"}, { kEventWindowFocusAcquired,"FocusAcquired"}, { kEventWindowFocusRelinquish,"FocusRelinquish"}, { kEventWindowDrawFrame,"DrawFrame"}, { kEventWindowDrawPart,"DrawPart"}, { kEventWindowGetRegion,"GetRegion"}, { kEventWindowHitTest,"HitTest"}, { kEventWindowInit,"Init"}, { kEventWindowDispose,"Dispose"}, { kEventWindowDragHilite,"DragHilite"}, { kEventWindowModified,"Modified"}, { kEventWindowSetupProxyDragImage,"SetupProxyDragImage"}, { kEventWindowStateChanged,"StateChanged"}, { kEventWindowMeasureTitle,"MeasureTitle"}, { kEventWindowDrawGrowBox,"DrawGrowBox"}, { kEventWindowGetGrowImageRegion,"GetGrowImageRegion"}, { kEventWindowPaint,"Paint"}, { 0, NULL }, }; static MyEventName mouseEventNames [] = { { kEventMouseMoved, "Moved"}, { kEventMouseUp, "Up"}, { kEventMouseDown, "Down"}, { kEventMouseDragged, "Dragged"}, { kEventMouseWheelMoved, "WheelMoved"}, { 0, NULL} }; static MyEventName keyboardEventNames [] = { { kEventRawKeyDown, "Down"}, { kEventRawKeyRepeat, "Repeat"}, { kEventRawKeyUp, "Up"}, { kEventRawKeyModifiersChanged, "ModifiersChanged"}, { kEventHotKeyPressed, "HotKeyPressed"}, { kEventHotKeyReleased, "HotKeyReleased"}, { 0, NULL} }; static MyEventName appEventNames [] = { { kEventAppActivated, "Activated"}, { kEventAppDeactivated, "Deactivated"}, { kEventAppQuit, "Quit"}, { kEventAppLaunchNotification, "LaunchNotification"}, { kEventAppLaunched, "Launched"}, { kEventAppTerminated, "Terminated"}, { kEventAppFrontSwitched, "FrontSwitched"}, { 0, NULL} }; static MyEventName menuEventNames [] = { { kEventMenuBeginTracking, "BeginTracking"}, { kEventMenuEndTracking, "EndTracking"}, { kEventMenuChangeTrackingMode, "ChangeTrackingMode"}, { kEventMenuOpening, "Opening"}, { kEventMenuClosed, "Closed"}, { kEventMenuTargetItem, "TargetItem"}, { kEventMenuMatchKey, "MatchKey"}, { kEventMenuEnableItems, "EnableItems"}, { kEventMenuDispose, "Dispose"}, { 0, NULL } }; static MyEventName controlEventNames [] = { { kEventControlInitialize, "Initialize" }, { kEventControlDispose, "Dispose" }, { kEventControlGetOptimalBounds, "GetOptimalBounds" }, { kEventControlHit, "Hit" }, { kEventControlSimulateHit, "SimulateHit" }, { kEventControlHitTest, "HitTest" }, { kEventControlDraw, "Draw" }, { kEventControlApplyBackground, "ApplyBackground" }, { kEventControlApplyTextColor, "ApplyTextColor" }, { kEventControlSetFocusPart, "SetFocusPart" }, { kEventControlGetFocusPart, "GetFocusPart" }, { kEventControlActivate, "Activate" }, { kEventControlDeactivate, "Deactivate" }, { kEventControlSetCursor, "SetCursor" }, { kEventControlContextualMenuClick, "ContextualMenuClick" }, { kEventControlClick, "Click" }, { kEventControlTrack, "Track" }, { kEventControlGetScrollToHereStartPoint, "GetScrollToHereStartPoint" }, { kEventControlGetIndicatorDragConstraint, "GetIndicatorDragConstraint" }, { kEventControlIndicatorMoved, "IndicatorMoved" }, { kEventControlGhostingFinished, "GhostingFinished" }, { kEventControlGetActionProcPart, "GetActionProcPart" }, { kEventControlGetPartRegion, "GetPartRegion" }, { kEventControlGetPartBounds, "GetPartBounds" }, { kEventControlSetData, "SetData" }, { kEventControlGetData, "GetData" }, { kEventControlValueFieldChanged, "ValueFieldChanged" }, { kEventControlAddedSubControl, "AddedSubControl" }, { kEventControlRemovingSubControl, "RemovingSubControl" }, { kEventControlBoundsChanged, "BoundsChanged" }, { kEventControlOwningWindowChanged, "OwningWindowChanged" }, { kEventControlArbitraryMessage, "ArbitraryMessage" }, { 0, NULL } }; static MyEventName commandEventNames [] = { { kEventCommandProcess, "Process" }, { kEventCommandUpdateStatus, "UpdateStatus" }, { 0, NULL } }; static MyEventNameList eventNameList [] = { { kEventClassWindow, windowEventNames }, { kEventClassMouse, mouseEventNames }, { kEventClassKeyboard, keyboardEventNames }, { kEventClassApplication, appEventNames }, { kEventClassMenu, menuEventNames }, { kEventClassControl, controlEventNames }, { kEventClassCommand, commandEventNames }, { 0, NULL} }; #ifdef TK_MACOSXDEBUG_UNUSED static MyEventName classicEventNames [] = { { nullEvent,"nullEvent" }, { mouseDown,"mouseDown" }, { mouseUp,"mouseUp" }, { keyDown,"keyDown" }, { keyUp,"keyUp" }, { autoKey,"autoKey" }, { updateEvt,"updateEvt" }, { diskEvt,"diskEvt" }, { activateEvt,"activateEvt" }, { osEvt,"osEvt" }, { kHighLevelEvent,"kHighLevelEvent" }, { 0, NULL } }; #endif /* TK_MACOSXDEBUG_UNUSED */ MODULE_SCOPE char * TkMacOSXCarbonEventToAscii(EventRef eventRef) { EventClass eventClass; EventKind eventKind; MyEventNameList * list = eventNameList; MyEventName * names = NULL; static char str[256]; char *buf = str; int *iPtr = (int*)str; int found = 0; eventClass = GetEventClass(eventRef); eventKind = GetEventKind(eventRef); *iPtr = eventClass; buf[4] = 0; strcat(buf, " "); buf += strlen(buf); while (list->names && (!names) ) { if (eventClass == list->c) { names = list -> names; } else { list++; } } while (names && names->name) { if (eventKind == names->kind) { snprintf(buf, 250, "%-20s", names->name); break; } else { names++; } } if (!found) { snprintf(buf, 250, "%-20d", eventKind); } return str; } #ifdef TK_MACOSXDEBUG_UNUSED MODULE_SCOPE char * TkMacOSXCarbonEventKindToAscii(EventRef eventRef, char * buf ) { EventClass eventClass; EventKind eventKind; MyEventNameList * list = eventNameList; MyEventName * names = NULL; int found = 0; eventClass = GetEventClass(eventRef); eventKind = GetEventKind(eventRef); while (list->names && (!names) ) { if (eventClass == list -> c) { names = list -> names; } else { list++; } } if (names) { found = 0; while ( names->name && !found ) { if (eventKind == names->kind) { sprintf(buf,"%s",names->name); found = 1; } else { names++; } } } if (!found) { sprintf ( buf,"%d", eventKind ); } else { sprintf ( buf,"%d", eventKind ); } return buf; } MODULE_SCOPE char * TkMacOSXClassicEventToAscii(EventRecord * eventPtr, char * buf ) { MyEventName * names = NULL; int found = 0; names = classicEventNames; while ( names -> name && !found ) if (eventPtr->what == names->kind) { int * iPtr; char cBuf[8]; iPtr=(int *) &cBuf; *iPtr = eventPtr->message; cBuf[4] = 0; sprintf(buf, "%-16s %08x %04x %s", names->name, (int) eventPtr->message, eventPtr->modifiers, cBuf); found = 1; } else { names++; } if (!found) { sprintf(buf,"%-16d %08x %08x, %s", eventPtr->what, (int) eventPtr->message, eventPtr->modifiers, buf); } return buf; } MODULE_SCOPE void TkMacOSXPrintPoint(char * tag, Point * p ) { TkMacOSXDbgMsg("%s %4d %4d", tag,p->h,p->v ); } MODULE_SCOPE void TkMacOSXPrintRect(char * tag, Rect * r ) { TkMacOSXDbgMsg("%s %4d %4d %4d %4d (%dx%d)", tag, r->left, r->top, r->right, r->bottom, r->right - r->left + 1, r->bottom - r->top + 1); } MODULE_SCOPE void TkMacOSXPrintRegion(char * tag, RgnHandle rgn ) { Rect r; GetRegionBounds(rgn,&r); TkMacOSXPrintRect(tag,&r); } MODULE_SCOPE void TkMacOSXPrintWindowTitle(char * tag, WindowRef window ) { Str255 title; GetWTitle(window,title); title [title[0] + 1] = 0; TkMacOSXDbgMsg("%s %s", tag, title +1 ); } typedef struct { int msg; char * name; } MsgName; static MsgName msgNames [] = { { kMenuDrawMsg, "Draw"}, { kMenuSizeMsg, "Size"}, { kMenuPopUpMsg, "PopUp"}, { kMenuCalcItemMsg, "CalcItem" }, { kMenuThemeSavvyMsg, "ThemeSavvy"}, { kMenuInitMsg, "Init" }, { kMenuDisposeMsg, "Dispose" }, { kMenuFindItemMsg, "FindItem" }, { kMenuHiliteItemMsg, "HiliteItem" }, { kMenuDrawItemsMsg, "DrawItems" }, { -1, NULL } }; MODULE_SCOPE char * TkMacOSXMenuMessageToAscii(int msg, char * s) { MsgName * msgNamePtr; for (msgNamePtr = msgNames;msgNamePtr->name;) { if (msgNamePtr->msg == msg) { strcpy(s,msgNamePtr->name); return s; } else { msgNamePtr++; } } sprintf(s,"unknown : %d", msg ); return s; } static MsgName trackingNames [] = { { kMouseTrackingMousePressed , "MousePressed " }, { kMouseTrackingMouseReleased , "MouseReleased " }, { kMouseTrackingMouseExited , "MouseExited " }, { kMouseTrackingMouseEntered , "MouseEntered " }, { kMouseTrackingMouseMoved , "MouseMoved " }, { kMouseTrackingKeyModifiersChanged, "KeyModifiersChanged" }, { kMouseTrackingUserCancelled , "UserCancelled " }, { kMouseTrackingTimedOut , "TimedOut " }, { -1, NULL } }; MODULE_SCOPE char * TkMacOSXMouseTrackingResultToAscii(MouseTrackingResult r, char * buf) { MsgName * namePtr; for (namePtr = trackingNames; namePtr->name; namePtr++) { if (namePtr->msg == r) { strcpy(buf, namePtr->name); return buf; } } sprintf(buf, "Unknown mouse tracking result : %d", r); return buf; } #endif /* TK_MACOSXDEBUG_UNUSED */ MODULE_SCOPE void TkMacOSXDebugFlashRegion( Drawable d, HIShapeRef rgn) { TkMacOSXInitNamedDebugSymbol(HIToolbox, int, QDDebugFlashRegion, CGrafPtr port, RgnHandle region); CFShow(rgn); if (d && rgn && QDDebugFlashRegion && !HIShapeIsEmpty(rgn)) { CGrafPtr port = TkMacOSXGetDrawablePort(d); if (port) { static RgnHandle qdRgn = NULL; if (!qdRgn) { qdRgn = NewRgn(); } ChkErr(HIShapeGetAsQDRgn, rgn, qdRgn); /* * Carbon-internal region flashing SPI (c.f. Technote 2124) */ QDDebugFlashRegion(port, qdRgn); SetEmptyRgn(qdRgn); } } } #include #include /* *---------------------------------------------------------------------- * * TkMacOSXGetNamedDebugSymbol -- * * * Dynamically acquire address of a named symbol from a loaded * dynamic library, so that we can use API that may not be * available on all OS versions. * For debugging purposes, if we cannot find the symbol with the * usual dynamic library APIs, we manually walk the symbol table * of the loaded library. This allows access to unexported * symbols such as private_extern internal debugging functions. * If module is NULL or the empty string, search all loaded * libraries (could be very expensive and should be avoided). * * THIS FUCTION IS ONLY TO BE USED FOR DEBUGGING PURPOSES, IT MAY * BREAK UNEXPECTEDLY IN THE FUTURE ! * * Results: * Address of given symbol or NULL if unavailable. * * Side effects: * None. * *---------------------------------------------------------------------- */ MODULE_SCOPE void * TkMacOSXGetNamedDebugSymbol( const char* module, const char* symbol) { void* addr = TkMacOSXGetNamedSymbol(module, symbol); #ifndef __LP64__ if (!addr) { const struct mach_header *mh = NULL; uint32_t i, n = _dyld_image_count(); size_t module_len = 0; if (module && *module) { module_len = strlen(module); } for (i = 0; i < n; i++) { if (module && *module) { /* Find image with given module name */ char *name; const char *path = _dyld_get_image_name(i); if (!path) { continue; } name = strrchr(path, '/') + 1; if (strncmp(name, module, module_len) != 0) { continue; } } mh = _dyld_get_image_header(i); if (mh) { struct load_command *lc; struct symtab_command *st = NULL; struct segment_command *sg = NULL; uint32_t j, m, nsect = 0, txtsectx = 0; lc = (struct load_command*)((const char*) mh + sizeof(struct mach_header)); m = mh->ncmds; for (j = 0; j < m; j++) { /* Find symbol table and index of __text section */ if (lc->cmd == LC_SEGMENT) { /* Find last segment before symbol table */ sg = (struct segment_command*) lc; if (!txtsectx) { /* Count total sections until (__TEXT, __text) */ uint32_t k, ns = sg->nsects; if (strcmp(sg->segname, SEG_TEXT) == 0) { struct section *s = (struct section *)( (char *)sg + sizeof(struct segment_command)); for(k = 0; k < ns; k++) { if (strcmp(s->sectname, SECT_TEXT) == 0) { txtsectx = nsect+k+1; break; } s++; } } nsect += ns; } } else if (!st && lc->cmd == LC_SYMTAB) { st = (struct symtab_command*) lc; break; } lc = (struct load_command *)((char *) lc + lc->cmdsize); } if (st && sg && txtsectx) { intptr_t base, slide = _dyld_get_image_vmaddr_slide(i); char *strings; struct nlist *sym; uint32_t strsize = st->strsize; int32_t strx; /* Offset file positions by difference to actual position in memory of last segment before symbol table: */ base = (intptr_t) sg->vmaddr + slide - sg->fileoff; strings = (char*)(base + st->stroff); sym = (struct nlist*)(base + st->symoff); m = st->nsyms; for (j = 0; j < m; j++) { /* Find symbol with given name in __text section */ strx = sym->n_un.n_strx; if ((sym->n_type & N_TYPE) == N_SECT && sym->n_sect == txtsectx && strx > 0 && (uint32_t) strx < strsize && strcmp(strings + strx, symbol) == 0) { addr = (char*) sym->n_value + slide; break; } sym++; } } } if (module && *module) { /* If given a module name, only search corresponding image */ break; } } } #endif /* __LP64__ */ return addr; } #endif /* TK_MAC_DEBUG */