xref: /reactos/win32ss/gdi/ntgdi/gdidbg.c (revision 560428e2)
1 /*
2  * PROJECT:         ReactOS win32 kernel mode subsystem
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            win32ss/gdi/ntgdi/gdidbg.c
5  * PURPOSE:         Special debugging functions for GDI
6  * PROGRAMMERS:     Timo Kreuzer
7  */
8 
9 /** INCLUDES ******************************************************************/
10 
11 #if DBG
12 #include <win32k.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 extern ULONG gulFirstFree;
17 extern ULONG gulFirstUnused;
18 extern PENTRY gpentHmgr;
19 
20 ULONG gulLogUnique = 0;
21 
22 /* Note: the following values need to be sorted */
23 DBG_CHANNEL DbgChannels[DbgChCount] = {
24     {L"EngBlt", DbgChEngBlt},
25     {L"EngBrush", DbgChEngBrush},
26     {L"EngClip", DbgChEngClip},
27     {L"EngCursor", DbgChEngCursor},
28     {L"EngDev", DbgChEngDev},
29     {L"EngErr", DbgChEngErr},
30     {L"EngEvent", DbgChEngEvent},
31     {L"EngGrad", DbgChEngGrad},
32     {L"EngLDev", DbgChEngLDev},
33     {L"EngLine", DbgChEngLine},
34     {L"EngMapping", DbgChEngMapping},
35     {L"EngMDev", DbgChEngMDev},
36     {L"EngPDev", DbgChEngPDev},
37     {L"EngSurface", DbgChEngSurface},
38     {L"EngWnd", DbgChEngWnd},
39     {L"EngXlate", DbgChEngXlate},
40     {L"GdiBitmap", DbgChGdiBitmap},
41     {L"GdiBlt", DbgChGdiBlt},
42     {L"GdiBrush", DbgChGdiBrush},
43     {L"GdiClipRgn", DbgChGdiClipRgn},
44     {L"GdiCoord", DbgChGdiCoord},
45     {L"GdiDC", DbgChGdiDC},
46     {L"GdiDCAttr", DbgChGdiDCAttr},
47     {L"GdiDCState", DbgChGdiDCState},
48     {L"GdiDev", DbgChGdiDev},
49     {L"GdiDib", DbgChGdiDib},
50     {L"GdiFont", DbgChGdiFont},
51     {L"GdiLine", DbgChGdiLine},
52     {L"GdiObj", DbgChGdiObj},
53     {L"GdiPalette", DbgChGdiPalette},
54     {L"GdiPath", DbgChGdiPath},
55     {L"GdiPen", DbgChGdiPen},
56     {L"GdiPool", DbgChGdiPool},
57     {L"GdiRgn", DbgChGdiRgn},
58     {L"GdiText", DbgChGdiText},
59     {L"GdiXFormObj", DbgChGdiXFormObj},
60     {L"UserAccel", DbgChUserAccel},
61     {L"UserCallback", DbgChUserCallback},
62     {L"UserCallProc", DbgChUserCallProc},
63     {L"UserCaret", DbgChUserCaret},
64     {L"UserClass", DbgChUserClass},
65     {L"UserClipbrd", DbgChUserClipbrd},
66     {L"UserCsr", DbgChUserCsr},
67     {L"UserDce", DbgChUserDce},
68     {L"UserDefwnd", DbgChUserDefwnd},
69     {L"UserDesktop", DbgChUserDesktop},
70     {L"UserDisplay",DbgChUserDisplay},
71     {L"UserEvent", DbgChUserEvent},
72     {L"UserFocus", DbgChUserFocus},
73     {L"UserHook", DbgChUserHook},
74     {L"UserHotkey", DbgChUserHotkey},
75     {L"UserIcon", DbgChUserIcon},
76     {L"UserInput", DbgChUserInput},
77     {L"UserKbd", DbgChUserKbd},
78     {L"UserKbdLayout", DbgChUserKbdLayout},
79     {L"UserMenu", DbgChUserMenu},
80     {L"UserMetric", DbgChUserMetric},
81     {L"UserMisc", DbgChUserMisc},
82     {L"UserMonitor", DbgChUserMonitor},
83     {L"UserMsg", DbgChUserMsg},
84     {L"UserMsgQ", DbgChUserMsgQ},
85     {L"UserObj", DbgChUserObj},
86     {L"UserPainting", DbgChUserPainting},
87     {L"UserProcess", DbgChUserProcess},
88     {L"UserPowerManager", DbgChUserPowerManager},
89     {L"UserProp", DbgChUserProp},
90     {L"UserScrollbar", DbgChUserScrollbar},
91     {L"UserSecurity", DbgChUserSecurity},
92     {L"UserShutdown", DbgChUserShutdown},
93     {L"UserSysparams", DbgChUserSysparams},
94     {L"UserTimer", DbgChUserTimer},
95     {L"UserThread", DbgChUserThread},
96     {L"UserWinpos", DbgChUserWinpos},
97     {L"UserWinsta", DbgChUserWinsta},
98     {L"UserWnd", DbgChUserWnd}
99 };
100 
101 ULONG
102 NTAPI
DbgCaptureStackBackTace(_Out_writes_ (cFramesToCapture)PVOID * ppvFrames,_In_ ULONG cFramesToSkip,_In_ ULONG cFramesToCapture)103 DbgCaptureStackBackTace(
104     _Out_writes_(cFramesToCapture) PVOID* ppvFrames,
105     _In_ ULONG cFramesToSkip,
106     _In_ ULONG cFramesToCapture)
107 {
108     ULONG cFrameCount;
109     PVOID apvTemp[30];
110     NT_ASSERT(cFramesToCapture <= _countof(apvTemp));
111 
112     /* Zero it out */
113     RtlZeroMemory(ppvFrames, cFramesToCapture * sizeof(PVOID));
114 
115     /* Capture kernel stack */
116     cFrameCount = RtlWalkFrameChain(apvTemp, _countof(apvTemp), 0);
117 
118     /* If we should skip more than we have, we are done */
119     if (cFramesToSkip > cFrameCount)
120         return 0;
121 
122     /* Copy, but skip frames */
123     cFrameCount -= cFramesToSkip;
124     cFrameCount = min(cFrameCount, cFramesToCapture);
125     RtlCopyMemory(ppvFrames, &apvTemp[cFramesToSkip], cFrameCount * sizeof(PVOID));
126 
127     /* Check if there is still space left */
128     if (cFrameCount < cFramesToCapture)
129     {
130         /* Capture user stack */
131         cFrameCount += RtlWalkFrameChain(&ppvFrames[cFrameCount],
132                                          cFramesToCapture - cFrameCount,
133                                          1);
134     }
135 
136     return cFrameCount;
137 }
138 
139 #if DBG_ENABLE_GDIOBJ_BACKTRACES
140 
141 static
142 BOOL
CompareBacktraces(USHORT idx1,USHORT idx2)143 CompareBacktraces(
144     USHORT idx1,
145     USHORT idx2)
146 {
147     POBJ pobj1, pobj2;
148     ULONG iLevel;
149 
150     /* Get the objects */
151     pobj1 = gpentHmgr[idx1].einfo.pobj;
152     pobj2 = gpentHmgr[idx2].einfo.pobj;
153 
154     /* Loop all stack levels */
155     for (iLevel = 0; iLevel < GDI_OBJECT_STACK_LEVELS; iLevel++)
156     {
157         /* If one level doesn't match we are done */
158         if (pobj1->apvBackTrace[iLevel] != pobj2->apvBackTrace[iLevel])
159         {
160             return FALSE;
161         }
162     }
163 
164     return TRUE;
165 }
166 
167 typedef struct
168 {
169     USHORT idx;
170     USHORT iCount;
171 } GDI_DBG_HANDLE_BT;
172 
173 VOID
174 NTAPI
DbgDumpGdiHandleTableWithBT(void)175 DbgDumpGdiHandleTableWithBT(void)
176 {
177     static BOOL bLeakReported = FALSE;
178     ULONG idx, j;
179     BOOL bAlreadyPresent;
180     GDI_DBG_HANDLE_BT aBacktraceTable[GDI_DBG_MAX_BTS];
181     USHORT iCount;
182     KIRQL OldIrql;
183     POBJ pobj;
184     ULONG iLevel, ulObj;
185 
186     /* Only report once */
187     if (bLeakReported)
188     {
189         DPRINT1("GDI handle abusers already reported!\n");
190         return;
191     }
192 
193     bLeakReported = TRUE;
194     DPRINT1("Reporting GDI handle abusers:\n");
195 
196     /* Zero out the table */
197     RtlZeroMemory(aBacktraceTable, sizeof(aBacktraceTable));
198 
199     /* We've got serious business to do */
200     KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
201 
202     /* Step through GDI handle table and find out who our culprit is... */
203     for (idx = RESERVE_ENTRIES_COUNT; idx < GDI_HANDLE_COUNT; idx++)
204     {
205         /* If the handle is free, continue */
206         if (gpentHmgr[idx].einfo.pobj == 0) continue;
207 
208         /* Check if this backtrace is already covered */
209         bAlreadyPresent = FALSE;
210         for (j = RESERVE_ENTRIES_COUNT; j < idx; j++)
211         {
212             if (CompareBacktraces(idx, j))
213             {
214                 bAlreadyPresent = TRUE;
215                 break;
216             }
217         }
218 
219         if (bAlreadyPresent) continue;
220 
221         /* We don't have this BT yet, count how often it is present */
222         iCount = 1;
223         for (j = idx + 1; j < GDI_HANDLE_COUNT; j++)
224         {
225             if (CompareBacktraces(idx, j))
226             {
227                 iCount++;
228             }
229         }
230 
231         /* Now add this backtrace */
232         for (j = 0; j < GDI_DBG_MAX_BTS; j++)
233         {
234             /* Insert it below the next smaller count */
235             if (aBacktraceTable[j].iCount < iCount)
236             {
237                 /* Check if there are entries above */
238                 if (j < GDI_DBG_MAX_BTS - 1)
239                 {
240                     /* Move the following entries up by 1 */
241                     RtlMoveMemory(&aBacktraceTable[j],
242                                   &aBacktraceTable[j + 1],
243                                   GDI_DBG_MAX_BTS - j - 1);
244                 }
245 
246                 /* Set this entry */
247                 aBacktraceTable[j].idx = idx;
248                 aBacktraceTable[j].iCount = iCount;
249 
250                 /* We are done here */
251                 break;
252             }
253         }
254     }
255 
256     /* Print the worst offenders... */
257     DbgPrint("Count Handle   Backtrace\n");
258     DbgPrint("------------------------------------------------\n");
259     for (j = 0; j < GDI_DBG_MAX_BTS; j++)
260     {
261         idx = aBacktraceTable[j].idx;
262         if (idx == 0)
263             break;
264 
265         ulObj = ((ULONG)gpentHmgr[idx].FullUnique << 16) | idx;
266         pobj = gpentHmgr[idx].einfo.pobj;
267 
268         DbgPrint("%5d %08lx ", aBacktraceTable[j].iCount, ulObj);
269         for (iLevel = 0; iLevel < GDI_OBJECT_STACK_LEVELS; iLevel++)
270         {
271             DbgPrint("%p,", pobj->apvBackTrace[iLevel]);
272         }
273         DbgPrint("\n");
274     }
275 
276     __debugbreak();
277 
278     KeLowerIrql(OldIrql);
279 }
280 
281 #endif /* DBG_ENABLE_GDIOBJ_BACKTRACES */
282 
283 #if DBG
284 
285 BOOL
286 NTAPI
DbgGdiHTIntegrityCheck(VOID)287 DbgGdiHTIntegrityCheck(VOID)
288 {
289 	ULONG i, nDeleted = 0, nFree = 0, nUsed = 0;
290 	PGDI_TABLE_ENTRY pEntry;
291 	BOOL r = 1;
292 
293 	KeEnterCriticalRegion();
294 
295 	/* FIXME: Check reserved entries */
296 
297 	/* Now go through the deleted objects */
298 	i = gulFirstFree & 0xffff;
299 	while (i)
300 	{
301 		pEntry = &GdiHandleTable->Entries[i];
302 		if (i >= GDI_HANDLE_COUNT)
303 		{
304 		    DPRINT1("nDeleted=%lu\n", nDeleted);
305 		    ASSERT(FALSE);
306 		}
307 
308         nDeleted++;
309 
310         /* Check the entry */
311         if ((pEntry->Type & GDI_ENTRY_BASETYPE_MASK) != 0)
312         {
313             r = 0;
314             DPRINT1("Deleted Entry has a type != 0\n");
315         }
316         if ((ULONG_PTR)pEntry->KernelData >= GDI_HANDLE_COUNT)
317         {
318             r = 0;
319             DPRINT1("Deleted entries KernelPointer too big\n");
320         }
321         if (pEntry->UserData != NULL)
322         {
323             r = 0;
324             DPRINT1("Deleted entry has UserData != 0\n");
325         }
326         if (pEntry->ProcessId != 0)
327         {
328             r = 0;
329             DPRINT1("Deleted entry has ProcessId != 0\n");
330         }
331 
332         i = (ULONG_PTR)pEntry->KernelData & 0xffff;
333 	};
334 
335 	for (i = gulFirstUnused;
336 	     i < GDI_HANDLE_COUNT;
337 	     i++)
338 	{
339 		pEntry = &GdiHandleTable->Entries[i];
340 
341 		if ((pEntry->Type) != 0)
342 		{
343 			r = 0;
344 			DPRINT1("Free Entry has a type != 0\n");
345 		}
346 		if ((ULONG_PTR)pEntry->KernelData != 0)
347 		{
348 			r = 0;
349 			DPRINT1("Free entries KernelPointer != 0\n");
350 		}
351 		if (pEntry->UserData != NULL)
352 		{
353 			r = 0;
354 			DPRINT1("Free entry has UserData != 0\n");
355 		}
356 		if (pEntry->ProcessId != 0)
357 		{
358 			r = 0;
359 			DPRINT1("Free entry has ProcessId != 0\n");
360 		}
361 		nFree++;
362 	}
363 
364 	for (i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++)
365 	{
366 		HGDIOBJ Handle;
367 		ULONG Type;
368 
369 		pEntry = &GdiHandleTable->Entries[i];
370 		Type = pEntry->Type;
371 		Handle = (HGDIOBJ)(ULONG_PTR)((Type << GDI_ENTRY_UPPER_SHIFT) + i);
372 
373 		if (Type & GDI_ENTRY_BASETYPE_MASK)
374 		{
375 			if (pEntry->KernelData == NULL)
376 			{
377 				r = 0;
378 				DPRINT1("Used entry has KernelData == 0\n");
379 			}
380 			else if (pEntry->KernelData <= MmHighestUserAddress)
381 			{
382 				r = 0;
383 				DPRINT1("Used entry invalid KernelData\n");
384 			}
385 			else if (((POBJ)(pEntry->KernelData))->hHmgr != Handle)
386 			{
387 				r = 0;
388 				DPRINT1("Used entry %lu, has invalid hHmg %p (expected: %p)\n",
389 				        i, ((POBJ)(pEntry->KernelData))->hHmgr, Handle);
390 			}
391 			nUsed++;
392 		}
393 	}
394 
395 	if (RESERVE_ENTRIES_COUNT + nDeleted + nFree + nUsed != GDI_HANDLE_COUNT)
396 	{
397 		r = 0;
398 		DPRINT1("Number of all entries incorrect: RESERVE_ENTRIES_COUNT = %lu, nDeleted = %lu, nFree = %lu, nUsed = %lu\n",
399 		        RESERVE_ENTRIES_COUNT, nDeleted, nFree, nUsed);
400 	}
401 
402 	KeLeaveCriticalRegion();
403 
404 	return r;
405 }
406 
407 #endif /* DBG */
408 
409 
410 #if DBG_ENABLE_EVENT_LOGGING
411 
412 VOID
413 NTAPI
DbgLogEvent(PSLIST_HEADER pslh,LOG_EVENT_TYPE nEventType,LPARAM lParam)414 DbgLogEvent(PSLIST_HEADER pslh, LOG_EVENT_TYPE nEventType, LPARAM lParam)
415 {
416     PLOGENTRY pLogEntry;
417 
418     /* Log a maximum of 100 events */
419     if (QueryDepthSList(pslh) >= 1000) return;
420 
421     /* Allocate a logentry */
422     pLogEntry = EngAllocMem(0, sizeof(LOGENTRY), 'golG');
423     if (!pLogEntry) return;
424 
425     /* Set type */
426     pLogEntry->nEventType = nEventType;
427     pLogEntry->ulUnique = InterlockedIncrement((LONG*)&gulLogUnique);
428     pLogEntry->dwProcessId = HandleToUlong(PsGetCurrentProcessId());
429     pLogEntry->dwThreadId = HandleToUlong(PsGetCurrentThreadId());
430     pLogEntry->lParam = lParam;
431 
432     /* Capture a backtrace */
433     DbgCaptureStackBackTace(pLogEntry->apvBackTrace, 1, 20);
434 
435     switch (nEventType)
436     {
437         case EVENT_ALLOCATE:
438         case EVENT_CREATE_HANDLE:
439         case EVENT_REFERENCE:
440         case EVENT_DEREFERENCE:
441         case EVENT_LOCK:
442         case EVENT_UNLOCK:
443         case EVENT_DELETE:
444         case EVENT_FREE:
445         case EVENT_SET_OWNER:
446         default:
447             break;
448     }
449 
450     /* Push it on the list */
451     InterlockedPushEntrySList(pslh, &pLogEntry->sleLink);
452 }
453 
454 #define REL_ADDR(va) ((ULONG_PTR)va - (ULONG_PTR)&__ImageBase)
455 
456 VOID
457 NTAPI
DbgPrintEvent(PLOGENTRY pLogEntry)458 DbgPrintEvent(PLOGENTRY pLogEntry)
459 {
460     PSTR pstr;
461 
462     switch (pLogEntry->nEventType)
463     {
464         case EVENT_ALLOCATE: pstr = "Allocate"; break;
465         case EVENT_CREATE_HANDLE: pstr = "CreatHdl"; break;
466         case EVENT_REFERENCE: pstr = "Ref"; break;
467         case EVENT_DEREFERENCE: pstr = "Deref"; break;
468         case EVENT_LOCK: pstr = "Lock"; break;
469         case EVENT_UNLOCK: pstr = "Unlock"; break;
470         case EVENT_DELETE: pstr = "Delete"; break;
471         case EVENT_FREE: pstr = "Free"; break;
472         case EVENT_SET_OWNER: pstr = "SetOwner"; break;
473         default: pstr = "Unknown"; break;
474     }
475 
476     DbgPrint("[%lu] %03x:%03x %.8s val=%p <%lx,%lx,%lx,%lx>\n",
477              pLogEntry->ulUnique,
478              pLogEntry->dwProcessId,
479              pLogEntry->dwThreadId,
480              pstr,
481              (PVOID)pLogEntry->lParam,
482              REL_ADDR(pLogEntry->apvBackTrace[2]),
483              REL_ADDR(pLogEntry->apvBackTrace[3]),
484              REL_ADDR(pLogEntry->apvBackTrace[4]),
485              REL_ADDR(pLogEntry->apvBackTrace[5]));
486 }
487 
488 VOID
489 NTAPI
DbgDumpEventList(PSLIST_HEADER pslh)490 DbgDumpEventList(PSLIST_HEADER pslh)
491 {
492     PSLIST_ENTRY psle;
493     PLOGENTRY pLogEntry;
494 
495     while ((psle = InterlockedPopEntrySList(pslh)))
496     {
497         pLogEntry = CONTAINING_RECORD(psle, LOGENTRY, sleLink);
498         DbgPrintEvent(pLogEntry);
499     }
500 }
501 
502 VOID
503 NTAPI
DbgCleanupEventList(PSLIST_HEADER pslh)504 DbgCleanupEventList(PSLIST_HEADER pslh)
505 {
506     PSLIST_ENTRY psle;
507     PLOGENTRY pLogEntry;
508 
509     while ((psle = InterlockedPopEntrySList(pslh)))
510     {
511         pLogEntry = CONTAINING_RECORD(psle, LOGENTRY, sleLink);
512         EngFreeMem(pLogEntry);
513     }
514 }
515 
516 #endif /* DBG_ENABLE_EVENT_LOGGING */
517 
518 #if 1 || DBG_ENABLE_SERVICE_HOOKS
519 
520 VOID
521 NTAPI
DbgDumpLockedGdiHandles(VOID)522 DbgDumpLockedGdiHandles(VOID)
523 {
524     ULONG i;
525 
526     for (i = RESERVE_ENTRIES_COUNT; i < GDI_HANDLE_COUNT; i++)
527     {
528         PENTRY pentry = &gpentHmgr[i];
529 
530         if (pentry->Objt)
531         {
532             POBJ pobj = pentry->einfo.pobj;
533             if (pobj->cExclusiveLock > 0)
534             {
535                 DPRINT1("Locked object: %lx, type = %lx. allocated from:\n",
536                         i, pentry->Objt);
537                 DBG_DUMP_EVENT_LIST(&pobj->slhLog);
538             }
539         }
540     }
541 }
542 
543 void
544 NTAPI
GdiDbgPreServiceHook(ULONG ulSyscallId,PULONG_PTR pulArguments)545 GdiDbgPreServiceHook(ULONG ulSyscallId, PULONG_PTR pulArguments)
546 {
547     PTHREADINFO pti = (PTHREADINFO)PsGetCurrentThreadWin32Thread();
548     if (pti && pti->cExclusiveLocks != 0)
549     {
550         DbgPrint("FATAL: Win32DbgPreServiceHook(0x%lx): There are %lu exclusive locks!\n",
551                  ulSyscallId, pti->cExclusiveLocks);
552         DbgDumpLockedGdiHandles();
553         ASSERT(FALSE);
554     }
555 
556 }
557 
558 ULONG_PTR
559 NTAPI
GdiDbgPostServiceHook(ULONG ulSyscallId,ULONG_PTR ulResult)560 GdiDbgPostServiceHook(ULONG ulSyscallId, ULONG_PTR ulResult)
561 {
562     PTHREADINFO pti = (PTHREADINFO)PsGetCurrentThreadWin32Thread();
563     if (pti && pti->cExclusiveLocks != 0)
564     {
565         DbgPrint("FATAL: Win32DbgPostServiceHook(0x%lx): There are %lu exclusive locks!\n",
566                  ulSyscallId, pti->cExclusiveLocks);
567         DbgDumpLockedGdiHandles();
568         ASSERT(FALSE);
569     }
570     return ulResult;
571 }
572 
573 #endif /* DBG_ENABLE_SERVICE_HOOKS */
574 
575 
576 NTSTATUS NTAPI
QueryEnvironmentVariable(PUNICODE_STRING Name,PUNICODE_STRING Value)577 QueryEnvironmentVariable(PUNICODE_STRING Name,
578                          PUNICODE_STRING Value)
579 {
580    NTSTATUS Status;
581    PWSTR wcs;
582    UNICODE_STRING var;
583    PWSTR val;
584    PPEB Peb;
585    PWSTR Environment;
586 
587    /* Ugly HACK for ReactOS system threads */
588    if(!NtCurrentTeb())
589    {
590        return(STATUS_VARIABLE_NOT_FOUND);
591    }
592 
593    Peb = NtCurrentPeb();
594 
595    if (Peb == NULL)
596    {
597        return(STATUS_VARIABLE_NOT_FOUND);
598    }
599 
600    Environment = Peb->ProcessParameters->Environment;
601 
602    if (Environment == NULL)
603    {
604       return(STATUS_VARIABLE_NOT_FOUND);
605    }
606 
607    Value->Length = 0;
608 
609    wcs = Environment;
610    while (*wcs)
611    {
612       var.Buffer = wcs++;
613       wcs = wcschr(wcs, L'=');
614       if (wcs == NULL)
615       {
616          wcs = var.Buffer + wcslen(var.Buffer);
617       }
618       if (*wcs)
619       {
620          var.Length = var.MaximumLength = (wcs - var.Buffer) * sizeof(WCHAR);
621          val = ++wcs;
622          wcs += wcslen(wcs);
623 
624          if (RtlEqualUnicodeString(&var, Name, TRUE))
625          {
626             Value->Length = (wcs - val) * sizeof(WCHAR);
627             if (Value->Length <= Value->MaximumLength)
628             {
629                memcpy(Value->Buffer, val,
630                       min(Value->Length + sizeof(WCHAR), Value->MaximumLength));
631                Status = STATUS_SUCCESS;
632             }
633             else
634             {
635                Status = STATUS_BUFFER_TOO_SMALL;
636             }
637 
638             return(Status);
639          }
640       }
641       wcs++;
642    }
643 
644    return(STATUS_VARIABLE_NOT_FOUND);
645 }
646 
647 static int __cdecl
DbgCompareChannels(const void * a,const void * b)648 DbgCompareChannels(const void * a, const void * b)
649 {
650     return wcscmp((WCHAR*)a, ((DBG_CHANNEL*)b)->Name);
651 }
652 
653 static BOOL
DbgAddDebugChannel(PPROCESSINFO ppi,WCHAR * channel,WCHAR * level,WCHAR op)654 DbgAddDebugChannel(PPROCESSINFO ppi, WCHAR* channel, WCHAR* level, WCHAR op)
655 {
656     DBG_CHANNEL *ChannelEntry;
657     UINT iLevel, iChannel;
658 
659     /* Special treatment for the "all" channel */
660     if (wcscmp(channel, L"all") == 0)
661     {
662         for (iChannel = 0; iChannel < DbgChCount; iChannel++)
663         {
664             DbgAddDebugChannel(ppi, DbgChannels[iChannel].Name, level, op);
665         }
666         return TRUE;
667     }
668 
669     ChannelEntry = (DBG_CHANNEL*)bsearch(channel,
670                                          DbgChannels,
671                                          DbgChCount,
672                                          sizeof(DBG_CHANNEL),
673                                          DbgCompareChannels);
674     if(ChannelEntry == NULL)
675     {
676         return FALSE;
677     }
678 
679     iChannel = ChannelEntry->Id;
680     ASSERT(iChannel < DbgChCount);
681 
682     if(level == NULL || *level == L'\0' ||wcslen(level) == 0 )
683         iLevel = MAX_LEVEL;
684     else if(wcsncmp(level, L"err", 3) == 0)
685         iLevel = ERR_LEVEL;
686     else if(wcsncmp(level, L"fixme", 5) == 0)
687         iLevel = FIXME_LEVEL;
688     else if(wcsncmp(level, L"warn", 4) == 0)
689         iLevel = WARN_LEVEL;
690     else if (wcsncmp(level, L"trace", 4) == 0)
691         iLevel = TRACE_LEVEL;
692     else
693         return FALSE;
694 
695     if(op==L'+')
696     {
697         DBG_ENABLE_CHANNEL(ppi, iChannel, iLevel);
698     }
699     else
700     {
701         DBG_DISABLE_CHANNEL(ppi, iChannel, iLevel);
702     }
703 
704     return TRUE;
705 }
706 
707 static BOOL
DbgParseDebugChannels(PPROCESSINFO ppi,PUNICODE_STRING Value)708 DbgParseDebugChannels(PPROCESSINFO ppi, PUNICODE_STRING Value)
709 {
710     WCHAR *str, *separator, *c, op;
711 
712     str = Value->Buffer;
713 
714     do
715     {
716         separator = wcschr(str, L',');
717         if(separator != NULL)
718             *separator = L'\0';
719 
720         c = wcschr(str, L'+');
721         if(c == NULL)
722             c = wcschr(str, L'-');
723 
724         if(c != NULL)
725         {
726             op = *c;
727             *c = L'\0';
728             c++;
729 
730             DbgAddDebugChannel(ppi, c, str, op);
731         }
732 
733         str = separator + 1;
734     }while(separator != NULL);
735 
736     return TRUE;
737 }
738 
DbgInitDebugChannels(VOID)739 BOOL DbgInitDebugChannels(VOID)
740 {
741     WCHAR valBuffer[100];
742     UNICODE_STRING Value;
743     UNICODE_STRING Name = RTL_CONSTANT_STRING(L"DEBUGCHANNEL");
744     NTSTATUS Status;
745     PPROCESSINFO ppi;
746     BOOL ret;
747 
748     /* Initialize all channels to ERROR */
749     ppi = PsGetCurrentProcessWin32Process();
750     RtlFillMemory( ppi->DbgChannelLevel,
751                    sizeof(ppi->DbgChannelLevel),
752                    ERR_LEVEL);
753 
754     /* Find DEBUGCHANNEL env var */
755     Value.Buffer = valBuffer;
756     Value.Length = 0;
757     Value.MaximumLength = sizeof(valBuffer);
758     Status = QueryEnvironmentVariable(&Name, &Value);
759 
760     /* It does not exist */
761     if(Status == STATUS_VARIABLE_NOT_FOUND)
762     {
763         /* There is nothing more to do */
764         return TRUE;
765     }
766 
767     /* If the buffer in the stack is not enough allocate it */
768     if(Status == STATUS_BUFFER_TOO_SMALL)
769     {
770         Value.Buffer = ExAllocatePool(PagedPool, Value.MaximumLength);
771         if(Value.Buffer == NULL)
772         {
773             return FALSE;
774         }
775 
776         /* Get the env var again */
777         Status = QueryEnvironmentVariable(&Name, &Value);
778     }
779 
780    /* Check for error */
781     if(!NT_SUCCESS(Status))
782     {
783         if(Value.Buffer != valBuffer)
784         {
785             ExFreePool(Value.Buffer);
786         }
787 
788         return FALSE;
789     }
790 
791     /* Parse the variable */
792     ret = DbgParseDebugChannels(ppi, &Value);
793 
794     /* Clean up */
795     if(Value.Buffer != valBuffer)
796     {
797         ExFreePool(Value.Buffer);
798     }
799 
800     return ret;
801 }
802 
803 
804 #endif // DBG
805 
806 /* EOF */
807