[NtGDI] Change TextOut
[reactos.git] / win32ss / gdi / ntgdi / gdidbg.c
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 */