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