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