Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1463 lines
32 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. tracedb.c
  5. Abstract:
  6. This module contains the implementation for the trace database
  7. module (hash table to store stack trace in USer/Kernel mode).
  8. Author:
  9. Silviu Calinoiu (SilviuC) 22-Feb-2000
  10. Revision History:
  11. --*/
  12. #include <nt.h>
  13. #include <ntrtl.h>
  14. #include <nturtl.h>
  15. #include <ntos.h>
  16. #include "tracedbp.h"
  17. //
  18. // TRACE_ASSERT
  19. //
  20. // SilviuC: should change this to normal ASSERT() macro when code gets
  21. // mature enough.
  22. //
  23. #if DBG
  24. #define TRACE_ASSERT(Expr) { \
  25. if (!(Expr)) { \
  26. DbgPrint ("Page heap: (%s, %d): \" %s \" -- assertion failed \n", \
  27. __FILE__, __LINE__, #Expr); \
  28. DbgBreakPoint (); \
  29. }}
  30. #else
  31. #define TRACE_ASSERT(Expr)
  32. #endif // #if DBG
  33. //
  34. // Magic values that prefix tracedb structures and allow
  35. // early detection of corruptions.
  36. //
  37. #define RTL_TRACE_BLOCK_MAGIC 0xABCDAAAA
  38. #define RTL_TRACE_SEGMENT_MAGIC 0xABCDBBBB
  39. #define RTL_TRACE_DATABASE_MAGIC 0xABCDCCCC
  40. //
  41. // Amount of memory with each a trace database will be
  42. // increased if a new trace cannot be stored.
  43. //
  44. #ifdef NTOS_KERNEL_RUNTIME
  45. #define RTL_TRACE_SIZE_INCREMENT PAGE_SIZE
  46. #else
  47. #define RTL_TRACE_SIZE_INCREMENT 0x10000
  48. #endif // #ifdef NTOS_KERNEL_RUNTIME
  49. //
  50. // Internal function declarations
  51. //
  52. BOOLEAN
  53. RtlpTraceDatabaseInternalAdd (
  54. IN PRTL_TRACE_DATABASE Database,
  55. IN ULONG Count,
  56. IN PVOID * Trace,
  57. OUT PRTL_TRACE_BLOCK * TraceBlock OPTIONAL
  58. );
  59. BOOLEAN
  60. RtlpTraceDatabaseInternalFind (
  61. PRTL_TRACE_DATABASE Database,
  62. IN ULONG Count,
  63. IN PVOID * Trace,
  64. OUT PRTL_TRACE_BLOCK * TraceBlock OPTIONAL
  65. );
  66. ULONG
  67. RtlpTraceStandardHashFunction (
  68. IN ULONG Count,
  69. IN PVOID * Trace
  70. );
  71. PVOID
  72. RtlpTraceDatabaseAllocate (
  73. IN SIZE_T Size,
  74. IN ULONG Flags, // OPTIONAL in User mode
  75. IN ULONG Tag // OPTIONAL in User mode
  76. );
  77. BOOLEAN
  78. RtlpTraceDatabaseFree (
  79. PVOID Block,
  80. IN ULONG Tag // OPTIONAL in User mode
  81. );
  82. BOOLEAN
  83. RtlpTraceDatabaseInitializeLock (
  84. IN PRTL_TRACE_DATABASE Database
  85. );
  86. BOOLEAN
  87. RtlpTraceDatabaseUninitializeLock (
  88. IN PRTL_TRACE_DATABASE Database
  89. );
  90. BOOLEAN
  91. RtlpTraceDatabaseAcquireLock (
  92. IN PRTL_TRACE_DATABASE Database
  93. );
  94. BOOLEAN
  95. RtlpTraceDatabaseReleaseLock (
  96. IN PRTL_TRACE_DATABASE Database
  97. );
  98. PRTL_TRACE_SEGMENT
  99. RtlpTraceSegmentCreate (
  100. IN SIZE_T Size,
  101. IN ULONG Flags, // OPTIONAL in User mode
  102. IN ULONG Tag // OPTIONAL in User mode
  103. );
  104. //
  105. // Trace database implementation
  106. //
  107. PRTL_TRACE_DATABASE
  108. RtlTraceDatabaseCreate (
  109. IN ULONG Buckets,
  110. IN SIZE_T MaximumSize OPTIONAL,
  111. IN ULONG Flags, // OPTIONAL in User mode
  112. IN ULONG Tag, // OPTIONAL in User mode
  113. IN RTL_TRACE_HASH_FUNCTION HashFunction OPTIONAL
  114. )
  115. /*++
  116. Routine Description:
  117. This routine creates a trace database, that is a hash table
  118. to store stack traces.
  119. Arguments:
  120. Buckets - no of buckets of the hash table with simple chaining.
  121. MaximumSize - maximum amount of memory that the database can use.
  122. When limit is hit, database add operations will start to fail.
  123. If the value is zero then no limit will be imposed.
  124. Flags - flags to control if allocation in K mode is done in P or NP
  125. pool. The possible bits that can be used right now are:
  126. RTL_TRACE_USE_PAGED_POOL
  127. RTL_TRACE_USE_NONPAGED_POOL
  128. Tag - tag used for K mode allocations.
  129. HashFunction - hash function to be used. If null passed a standard hash
  130. function will be provided by the module.
  131. Return Value:
  132. Pointer to an initialized trace database structure.
  133. Environment:
  134. Any.
  135. --*/
  136. {
  137. PVOID RawArea;
  138. SIZE_T RawSize;
  139. PRTL_TRACE_DATABASE Database;
  140. PRTL_TRACE_SEGMENT Segment;
  141. ULONG FirstFlags;
  142. //
  143. // Prepare trace database flags. The first segment of
  144. // the database will be allocated in nonpaged pool
  145. // no matter what flags are used because it contains
  146. // kernel synchronization objects that need to be in
  147. // that pool.
  148. //
  149. #ifdef NTOS_KERNEL_RUNTIME
  150. Flags |= RTL_TRACE_IN_KERNEL_MODE;
  151. FirstFlags = RTL_TRACE_IN_KERNEL_MODE | RTL_TRACE_USE_NONPAGED_POOL;
  152. #else
  153. Flags |= RTL_TRACE_IN_USER_MODE;
  154. FirstFlags = Flags;
  155. #endif // #ifdef NTOS_KERNEL_RUNTIME
  156. //
  157. // Allocate first segment of trace database that will contain
  158. // DATABASE, SEGMENT, buckets of the hash table and later traces.
  159. //
  160. RawSize = sizeof (RTL_TRACE_DATABASE) +
  161. sizeof (RTL_TRACE_SEGMENT) +
  162. Buckets * sizeof (PRTL_TRACE_BLOCK);
  163. RawSize += RTL_TRACE_SIZE_INCREMENT;
  164. RawSize &= ~(RTL_TRACE_SIZE_INCREMENT - 1);
  165. RawArea = RtlpTraceDatabaseAllocate (
  166. RawSize,
  167. FirstFlags,
  168. Tag);
  169. if (RawArea == NULL) {
  170. return NULL;
  171. }
  172. Database = (PRTL_TRACE_DATABASE)RawArea;
  173. Segment = (PRTL_TRACE_SEGMENT)(Database + 1);
  174. //
  175. // Initialize the database
  176. //
  177. Database->Magic = RTL_TRACE_DATABASE_MAGIC;
  178. Database->Flags = Flags;
  179. Database->Tag = Tag;
  180. Database->SegmentList = NULL;
  181. Database->MaximumSize = MaximumSize;
  182. Database->CurrentSize = RTL_TRACE_SIZE_INCREMENT;
  183. Database->Owner = NULL;
  184. Database->NoOfHits = 0;
  185. Database->NoOfTraces = 0;
  186. RtlZeroMemory (Database->HashCounter, sizeof (Database->HashCounter));
  187. if (! RtlpTraceDatabaseInitializeLock (Database)) {
  188. RtlpTraceDatabaseFree (RawArea, Tag);
  189. return NULL;
  190. }
  191. Database->NoOfBuckets = Buckets;
  192. if (HashFunction == NULL) {
  193. Database->HashFunction = RtlpTraceStandardHashFunction;
  194. }
  195. else {
  196. Database->HashFunction = HashFunction;
  197. }
  198. //
  199. // Initialize first segment of the database
  200. //
  201. Segment->Magic = RTL_TRACE_SEGMENT_MAGIC;
  202. Segment->Database = Database;
  203. Segment->NextSegment = NULL;
  204. Segment->TotalSize = RTL_TRACE_SIZE_INCREMENT;
  205. Database->SegmentList = Segment;
  206. //
  207. // Initialize the buckets of the database.
  208. //
  209. Database->Buckets = (PRTL_TRACE_BLOCK *)(Segment + 1);
  210. RtlZeroMemory (Database->Buckets, Database->NoOfBuckets * sizeof(PRTL_TRACE_BLOCK));
  211. //
  212. // Initialize free pointer for segment
  213. //
  214. Segment->SegmentStart = (PCHAR)RawArea;
  215. Segment->SegmentEnd = Segment->SegmentStart + RTL_TRACE_SIZE_INCREMENT;
  216. Segment->SegmentFree = (PCHAR)(Segment + 1) + Database->NoOfBuckets * sizeof(PRTL_TRACE_BLOCK);
  217. return Database;
  218. }
  219. BOOLEAN
  220. RtlTraceDatabaseDestroy (
  221. IN PRTL_TRACE_DATABASE Database
  222. )
  223. /*++
  224. Routine Description:
  225. This routine destroys a trace database. It takes care of
  226. deallocating everything, uninitializing synchronization
  227. objects, etc.
  228. Arguments:
  229. Database - trace database
  230. Return Value:
  231. TRUE if destroy operation was successful. FALSE otherwise.
  232. Environment:
  233. Any.
  234. --*/
  235. {
  236. PRTL_TRACE_SEGMENT Current;
  237. BOOLEAN Success;
  238. BOOLEAN SomethingFailed = FALSE;
  239. PRTL_TRACE_SEGMENT NextSegment;
  240. //
  241. // Sanity checks.
  242. //
  243. TRACE_ASSERT (Database && Database->Magic == RTL_TRACE_DATABASE_MAGIC);
  244. TRACE_ASSERT (RtlTraceDatabaseValidate (Database));
  245. //
  246. // Uninitialize the database lock. Even if we fail
  247. // we will continue to release memory for all segments.
  248. //
  249. // N.B. We cannot acquire the lock here for the last time because this
  250. // has as a side effect elevating the irql (in K mode) and then the
  251. // function will return with raised irql.
  252. //
  253. Success = RtlpTraceDatabaseUninitializeLock (Database);
  254. if (! Success) {
  255. SomethingFailed = TRUE;
  256. }
  257. //
  258. // Traverse the list of segments and release memory one by one.
  259. // Special attention with the last segment because it contains
  260. // the database structure itself and we do not want to shoot.
  261. // ourselves in the foot.
  262. //
  263. for (Current = Database->SegmentList;
  264. Current != NULL;
  265. Current = NextSegment) {
  266. //
  267. // We save the next segment before freeing the structure.
  268. //
  269. NextSegment = Current->NextSegment;
  270. //
  271. // If this is the last segment we need to offset Current pointer
  272. // by the size of the database structure.
  273. //
  274. if (NextSegment == NULL) {
  275. Current = (PRTL_TRACE_SEGMENT) ((PRTL_TRACE_DATABASE)Current - 1);
  276. }
  277. Success = RtlpTraceDatabaseFree (Current, Database->Tag);
  278. if (! Success) {
  279. DbgPrint ("Trace database: failed to release segment %p \n", Current);
  280. SomethingFailed = TRUE;
  281. }
  282. }
  283. if (SomethingFailed) {
  284. return FALSE;
  285. }
  286. else {
  287. return TRUE;
  288. }
  289. }
  290. BOOLEAN
  291. RtlTraceDatabaseValidate (
  292. IN PRTL_TRACE_DATABASE Database
  293. )
  294. /*++
  295. Routine Description:
  296. This routine validates the correctness of a trace database.
  297. It is intended to be used for testing purposes.
  298. Arguments:
  299. Database - trace database
  300. Return Value:
  301. TRUE if the database is ok. For most of the inconsistencies this
  302. function will break in the debugger.
  303. Environment:
  304. Any.
  305. --*/
  306. {
  307. PRTL_TRACE_SEGMENT Segment;
  308. PRTL_TRACE_BLOCK Block;
  309. ULONG Index;
  310. TRACE_ASSERT (Database != NULL);
  311. TRACE_ASSERT (Database->Magic == RTL_TRACE_DATABASE_MAGIC);
  312. RtlpTraceDatabaseAcquireLock (Database);
  313. //
  314. // Check all segments.
  315. //
  316. for (Segment = Database->SegmentList;
  317. Segment != NULL;
  318. Segment = Segment->NextSegment) {
  319. TRACE_ASSERT (Segment->Magic == RTL_TRACE_SEGMENT_MAGIC);
  320. }
  321. //
  322. // Check all blocks.
  323. //
  324. for (Index = 0; Index < Database->NoOfBuckets; Index++) {
  325. for (Block = Database->Buckets[Index];
  326. Block != NULL;
  327. Block = Block->Next) {
  328. TRACE_ASSERT (Block->Magic == RTL_TRACE_BLOCK_MAGIC);
  329. }
  330. }
  331. RtlpTraceDatabaseReleaseLock (Database);
  332. return TRUE;
  333. }
  334. BOOLEAN
  335. RtlTraceDatabaseAdd (
  336. IN PRTL_TRACE_DATABASE Database,
  337. IN ULONG Count,
  338. IN PVOID * Trace,
  339. OUT PRTL_TRACE_BLOCK * TraceBlock OPTIONAL
  340. )
  341. /*++
  342. Routine Description:
  343. This routine adds a new stack trace to the database. If the trace
  344. already exists then only the `Count' field for the trace will be
  345. incremented.
  346. Arguments:
  347. Database - trace database
  348. Count - number of pointers (PVOIDs) in the trace
  349. Trace - array of PVOIDs (the trace)
  350. TraceBlock - if not null will contain the address of the block where
  351. the trace was stored.
  352. Return Value:
  353. TRUE if a trace was added to the database. TraceBlock will contain
  354. the address of the block. If the trace was already present in the
  355. database a block with `Count' greater than 1 will be returned.
  356. Environment:
  357. Any.
  358. --*/
  359. {
  360. BOOLEAN Result;
  361. //
  362. // Sanity checks.
  363. //
  364. TRACE_ASSERT (Database && Database->Magic == RTL_TRACE_DATABASE_MAGIC);
  365. RtlpTraceDatabaseAcquireLock (Database);
  366. Result = RtlpTraceDatabaseInternalAdd (
  367. Database,
  368. Count,
  369. Trace,
  370. TraceBlock);
  371. RtlpTraceDatabaseReleaseLock (Database);
  372. return Result;
  373. }
  374. BOOLEAN
  375. RtlTraceDatabaseFind (
  376. PRTL_TRACE_DATABASE Database,
  377. IN ULONG Count,
  378. IN PVOID * Trace,
  379. OUT PRTL_TRACE_BLOCK * TraceBlock OPTIONAL
  380. )
  381. /*++
  382. Routine Description:
  383. This routine searches a trace in the database. If the trace
  384. is found then the address of the block that stores the trace
  385. will be returned.
  386. Arguments:
  387. Database - trace database
  388. Count - number of pointers (PVOIDs) in the trace
  389. Trace - array of PVOIDs (the trace)
  390. TraceBlock - if not null will contain the address of the block where
  391. the trace is stored.
  392. Return Value:
  393. TRUE if the trace was found in the database. TraceBlock will contain
  394. the address of the block that stores the trace.
  395. Environment:
  396. Any.
  397. --*/
  398. {
  399. BOOLEAN Result;
  400. //
  401. // Sanity checks.
  402. //
  403. TRACE_ASSERT (Database && Database->Magic == RTL_TRACE_DATABASE_MAGIC);
  404. RtlpTraceDatabaseAcquireLock (Database);
  405. Result = RtlpTraceDatabaseInternalFind (
  406. Database,
  407. Count,
  408. Trace,
  409. TraceBlock);
  410. if (Result) {
  411. Database->NoOfHits += 1;
  412. }
  413. RtlpTraceDatabaseReleaseLock (Database);
  414. return Result;
  415. }
  416. BOOLEAN
  417. RtlpTraceDatabaseInternalAdd (
  418. IN PRTL_TRACE_DATABASE Database,
  419. IN ULONG Count,
  420. IN PVOID * Trace,
  421. OUT PRTL_TRACE_BLOCK * TraceBlock OPTIONAL
  422. )
  423. /*++
  424. Routine Description:
  425. This is the internal routine to add a trace. See RtlTraceDatabaseAdd
  426. for more details.
  427. Arguments:
  428. Database - trace database
  429. Count - size of trace (in PVOIDs)
  430. Trace - trace
  431. TraceBlock - address of block where the trace is stored
  432. Return Value:
  433. TRUE if trace was added.
  434. Environment:
  435. Called from RtlTraceDatabaseAdd. Assumes the database lock
  436. is held.
  437. --*/
  438. {
  439. PRTL_TRACE_BLOCK Block;
  440. PRTL_TRACE_SEGMENT Segment;
  441. PRTL_TRACE_SEGMENT TopSegment;
  442. ULONG HashValue;
  443. SIZE_T RequestSize;
  444. //
  445. // Check if the block is already in the database (hash table).
  446. // If it is increase the number of hits and return.
  447. //
  448. if (RtlpTraceDatabaseInternalFind (Database, Count, Trace, &Block)) {
  449. Block->Count += 1;
  450. if (TraceBlock) {
  451. *TraceBlock = Block;
  452. }
  453. Database->NoOfHits += 1;
  454. return TRUE;
  455. }
  456. //
  457. // We need to create a new block. First we need to figure out
  458. // if the current segment can accomodate the new block.
  459. //
  460. RequestSize = sizeof(*Block) + Count * sizeof(PVOID);
  461. TopSegment = Database->SegmentList;
  462. if (RequestSize > (SIZE_T)(TopSegment->SegmentEnd - TopSegment->SegmentFree)) {
  463. //
  464. // If the database has a maximum size and that limit
  465. // has been reached then fail the call.
  466. //
  467. if (Database->MaximumSize > 0) {
  468. if (Database->CurrentSize > Database->MaximumSize) {
  469. if (TraceBlock) {
  470. *TraceBlock = NULL;
  471. }
  472. return FALSE;
  473. }
  474. }
  475. //
  476. // Allocate a new database segment. Fail call if cannot
  477. // allocate.
  478. //
  479. Segment = RtlpTraceSegmentCreate (RTL_TRACE_SIZE_INCREMENT,
  480. Database->Flags,
  481. Database->Tag);
  482. if (Segment == NULL) {
  483. if (TraceBlock) {
  484. *TraceBlock = NULL;
  485. }
  486. return FALSE;
  487. }
  488. //
  489. // Add the new segment to the database.
  490. //
  491. Segment->Magic = RTL_TRACE_SEGMENT_MAGIC;
  492. Segment->Database = Database;
  493. Segment->TotalSize = RTL_TRACE_SIZE_INCREMENT;
  494. Segment->SegmentStart = (PCHAR)Segment;
  495. Segment->SegmentEnd = Segment->SegmentStart + RTL_TRACE_SIZE_INCREMENT;
  496. Segment->SegmentFree = (PCHAR)(Segment + 1);
  497. Segment->NextSegment = Database->SegmentList;
  498. Database->SegmentList = Segment;
  499. TopSegment = Database->SegmentList;
  500. Database->CurrentSize += RTL_TRACE_SIZE_INCREMENT;
  501. }
  502. if (RequestSize > (SIZE_T)(TopSegment->SegmentEnd - TopSegment->SegmentFree)) {
  503. DbgPrint ("Trace database: failing attempt to save biiiiig trace (size %u) \n",
  504. Count);
  505. if (TraceBlock) {
  506. *TraceBlock = NULL;
  507. }
  508. return FALSE;
  509. }
  510. //
  511. // Finaly we can allocate our block.
  512. //
  513. Block = (PRTL_TRACE_BLOCK)(TopSegment->SegmentFree);
  514. TopSegment->SegmentFree += RequestSize;
  515. //
  516. // Fill the block with the new trace.
  517. //
  518. Block->Magic = RTL_TRACE_BLOCK_MAGIC;
  519. Block->Size = Count;
  520. Block->Count = 1;
  521. Block->Trace = (PVOID *)(Block + 1);
  522. Block->UserCount = 0;
  523. Block->UserSize = 0;
  524. //
  525. // Copy the trace
  526. //
  527. RtlMoveMemory (Block->Trace, Trace, Count * sizeof(PVOID));
  528. //
  529. // Add the block to corresponding bucket.
  530. //
  531. HashValue = (Database->HashFunction) (Count, Trace);
  532. HashValue %= Database->NoOfBuckets;
  533. Database->HashCounter[HashValue / (Database->NoOfBuckets / 16)] += 1;
  534. Block->Next = Database->Buckets[HashValue];
  535. Database->Buckets[HashValue] = Block;
  536. //
  537. // Loooong function. Finally return succes.
  538. //
  539. if (TraceBlock) {
  540. *TraceBlock = Block;
  541. }
  542. Database->NoOfTraces += 1;
  543. return TRUE;
  544. }
  545. BOOLEAN
  546. RtlpTraceDatabaseInternalFind (
  547. PRTL_TRACE_DATABASE Database,
  548. IN ULONG Count,
  549. IN PVOID * Trace,
  550. OUT PRTL_TRACE_BLOCK * TraceBlock OPTIONAL
  551. )
  552. /*++
  553. Routine Description:
  554. This internal routine searches for a trace in the database.
  555. Arguments:
  556. Database - trace database
  557. Count - size of trace (in PVOIDs)
  558. Trace - trace
  559. TraceBlock - element where the trace is stored.
  560. Return Value:
  561. TRUE if the trace was found.
  562. Environment:
  563. Called from RtlTraceDatabaseFind. Assumes the database lock is held.
  564. --*/
  565. {
  566. ULONG HashValue;
  567. PRTL_TRACE_BLOCK Current;
  568. ULONG Index;
  569. ULONG RequestSize;
  570. //
  571. // Find the bucket to search into.
  572. //
  573. HashValue = (Database->HashFunction) (Count, Trace);
  574. Database->HashCounter[HashValue % 16] += 1;
  575. HashValue %= Database->NoOfBuckets;
  576. //
  577. // Traverse the list of blocks for the found bucket
  578. //
  579. for (Current = Database->Buckets[HashValue];
  580. Current != NULL;
  581. Current = Current->Next) {
  582. //
  583. // If the size of the trace matches we might have a chance
  584. // to find an equal trace.
  585. //
  586. if (Count == Current->Size) {
  587. //
  588. // Figure out if the whole trace matches.
  589. //
  590. for (Index = 0; Index < Count; Index++) {
  591. if (Current->Trace[Index] != Trace[Index]) {
  592. break;
  593. }
  594. }
  595. //
  596. // If the trace matched completely we have found an entry.
  597. //
  598. if (Index == Count) {
  599. if (TraceBlock) {
  600. *TraceBlock = Current;
  601. }
  602. return TRUE;
  603. }
  604. }
  605. }
  606. //
  607. // If we traversed the whole list for the hashed bucket and did not
  608. // find anything we will fail the call.
  609. //
  610. if (TraceBlock) {
  611. *TraceBlock = NULL;
  612. }
  613. return FALSE;
  614. }
  615. ULONG
  616. RtlpTraceStandardHashFunction (
  617. IN ULONG Count,
  618. IN PVOID * Trace
  619. )
  620. /*++
  621. Routine Description:
  622. This routine is a simple hash function for stack traces in
  623. the case the caller of RtlTraceDatabaseCreate does not provide
  624. one. The function just xor's together all the pointers in the
  625. trace.
  626. Arguments:
  627. Count - size of trace (in PVOIDs)
  628. Trace - trace
  629. Return Value:
  630. Hash value. This needs to be reduced to the number of buckets
  631. in the hash table by a modulo operation (or something similar).
  632. Environment:
  633. Called internally by RtlpTraceDatabaseInternalAdd/Find.
  634. --*/
  635. {
  636. ULONG_PTR Value = 0;
  637. ULONG Index;
  638. unsigned short * Key;
  639. Key = (unsigned short *)Trace;
  640. for (Index = 0; Index < Count * (sizeof (PVOID) / sizeof(*Key)); Index += 2) {
  641. Value += Key[Index] ^ Key[Index + 1];
  642. }
  643. return PtrToUlong ((PVOID)Value);
  644. }
  645. PVOID
  646. RtlpTraceDatabaseAllocate (
  647. IN SIZE_T Size,
  648. IN ULONG Flags, // OPTIONAL in User mode
  649. IN ULONG Tag // OPTIONAL in User mode
  650. )
  651. /*++
  652. Routine Description:
  653. This routine allocates memory and hides all the different
  654. details for User vs Kernel mode allocation and paged vs
  655. nonpaged pool.
  656. Arguments:
  657. Size - size in bytes
  658. Flags - flags (specify U/K mode and P/NP pool)
  659. Tag - tag used for K mode allocations
  660. Return Value:
  661. Pointer to memory area allocated or null.
  662. Environment:
  663. Internal function for trace database module.
  664. --*/
  665. {
  666. #ifdef NTOS_KERNEL_RUNTIME
  667. //
  668. // SilviuC: should take a look if I can allocate with low
  669. // priority here (allocate with priority in pool).
  670. //
  671. if ((Flags & RTL_TRACE_USE_NONPAGED_POOL)) {
  672. return ExAllocatePoolWithTag (NonPagedPool, Size, Tag);
  673. }
  674. else {
  675. return ExAllocatePoolWithTag (PagedPool, Size, Tag);
  676. }
  677. #else
  678. NTSTATUS Status;
  679. PVOID RequestAddress;
  680. SIZE_T RequestSize;
  681. RequestAddress = NULL;
  682. RequestSize = Size;
  683. Status = NtAllocateVirtualMemory (
  684. NtCurrentProcess (),
  685. &RequestAddress,
  686. 0,
  687. &RequestSize,
  688. MEM_RESERVE | MEM_COMMIT,
  689. PAGE_READWRITE);
  690. if (NT_SUCCESS(Status)) {
  691. return RequestAddress;
  692. }
  693. else {
  694. return NULL;
  695. }
  696. #endif // #ifdef NTOS_KERNEL_RUNTIME
  697. }
  698. BOOLEAN
  699. RtlpTraceDatabaseFree (
  700. PVOID Block,
  701. IN ULONG Tag // OPTIONAL in User mode
  702. )
  703. /*++
  704. Routine Description:
  705. This routine frees memory and hides all the different
  706. details for User vs Kernel mode allocation and paged vs
  707. nonpaged pool.
  708. Arguments:
  709. Block - memory area to free
  710. Tag - tag used for K mode allocation
  711. Return Value:
  712. TRUE if deallocation was successful.
  713. Environment:
  714. Internal function for trace database module.
  715. --*/
  716. {
  717. #ifdef NTOS_KERNEL_RUNTIME
  718. ExFreePoolWithTag (Block, Tag);
  719. return TRUE;
  720. #else
  721. NTSTATUS Status;
  722. PVOID Address;
  723. SIZE_T Size;
  724. Address = Block;
  725. Size = 0;
  726. Status = NtFreeVirtualMemory (
  727. NtCurrentProcess (),
  728. &Address,
  729. &Size,
  730. MEM_RELEASE);
  731. if (NT_SUCCESS(Status)) {
  732. return TRUE;
  733. }
  734. else {
  735. return FALSE;
  736. }
  737. #endif // #ifdef NTOS_KERNEL_RUNTIME
  738. }
  739. BOOLEAN
  740. RtlpTraceDatabaseInitializeLock (
  741. IN PRTL_TRACE_DATABASE Database
  742. )
  743. /*++
  744. Routine Description:
  745. This routine initializes the trace database lock.
  746. It hides all details about the actual nature of the lock.
  747. Arguments:
  748. Database - trace database
  749. Return Value:
  750. TRUE if successful.
  751. Environment:
  752. Internal trace database module function.
  753. --*/
  754. {
  755. #ifdef NTOS_KERNEL_RUNTIME
  756. ASSERT((Database->Flags & RTL_TRACE_IN_KERNEL_MODE));
  757. if ((Database->Flags & RTL_TRACE_USE_NONPAGED_POOL)) {
  758. KeInitializeSpinLock (&(Database->u.SpinLock));
  759. }
  760. else {
  761. ExInitializeFastMutex (&(Database->u.FastMutex));
  762. }
  763. return TRUE;
  764. #else
  765. ASSERT((Database->Flags & RTL_TRACE_IN_USER_MODE));
  766. RtlInitializeCriticalSection (&(Database->Lock));
  767. return TRUE;
  768. #endif // #ifdef NTOS_KERNEL_RUNTIME
  769. }
  770. BOOLEAN
  771. RtlpTraceDatabaseUninitializeLock (
  772. IN PRTL_TRACE_DATABASE Database
  773. )
  774. /*++
  775. Routine Description:
  776. This routine uninitializes the trace database lock.
  777. It hides all details about the actual nature of the lock.
  778. (e.g. In user mode we need to call RtlDeleteCriticalSection).
  779. Arguments:
  780. Database - trace database
  781. Return Value:
  782. TRUE if successful.
  783. Environment:
  784. Internal trace database module function.
  785. --*/
  786. {
  787. #ifdef NTOS_KERNEL_RUNTIME
  788. ASSERT((Database->Flags & RTL_TRACE_IN_KERNEL_MODE));
  789. if ((Database->Flags & RTL_TRACE_USE_NONPAGED_POOL)) {
  790. //
  791. // No "uninitialize" required for spinlocks.
  792. //
  793. }
  794. else {
  795. //
  796. // No "uninitialize" required for fast mutexes.
  797. //
  798. }
  799. return TRUE;
  800. #else
  801. ASSERT((Database->Flags & RTL_TRACE_IN_USER_MODE));
  802. RtlDeleteCriticalSection (&(Database->Lock));
  803. return TRUE;
  804. #endif // #ifdef NTOS_KERNEL_RUNTIME
  805. }
  806. VOID
  807. RtlTraceDatabaseLock (
  808. IN PRTL_TRACE_DATABASE Database
  809. )
  810. /*++
  811. Routine Description:
  812. This routine acquires the trace database lock.
  813. It hides all details about the actual nature of the lock.
  814. The callers needs to acquire the database lock only if
  815. a trace block will be modified (UserCount, UserSize fields).
  816. The lock is not needed for Add/Find/Enumerate operations.
  817. Arguments:
  818. Database - trace database
  819. Return Value:
  820. None.
  821. Environment:
  822. Called if a trace block will be modified.
  823. --*/
  824. {
  825. RtlpTraceDatabaseAcquireLock(Database);
  826. }
  827. VOID
  828. RtlTraceDatabaseUnlock (
  829. IN PRTL_TRACE_DATABASE Database
  830. )
  831. /*++
  832. Routine Description:
  833. This routine releases the trace database lock.
  834. It hides all details about the actual nature of the lock.
  835. The callers needs to acquire/release the database lock only if
  836. a trace block will be modified (UserCount, UserSize fields).
  837. The lock is not needed for Add/Find/Enumerate operations.
  838. Arguments:
  839. Database - trace database
  840. Return Value:
  841. None.
  842. Environment:
  843. Called if a trace block will be modified.
  844. --*/
  845. {
  846. RtlpTraceDatabaseReleaseLock(Database);
  847. }
  848. BOOLEAN
  849. RtlpTraceDatabaseAcquireLock (
  850. IN PRTL_TRACE_DATABASE Database
  851. )
  852. /*++
  853. Routine Description:
  854. This routine acquires the trace database lock.
  855. It hides all details about the actual nature of the lock.
  856. Arguments:
  857. Database - trace database
  858. Return Value:
  859. TRUE if successful.
  860. Environment:
  861. Internal trace database module function.
  862. --*/
  863. {
  864. #ifdef NTOS_KERNEL_RUNTIME
  865. ASSERT((Database->Flags & RTL_TRACE_IN_KERNEL_MODE));
  866. if ((Database->Flags & RTL_TRACE_USE_NONPAGED_POOL)) {
  867. KeAcquireSpinLock (&(Database->u.SpinLock), &(Database->SavedIrql));
  868. }
  869. else {
  870. ExAcquireFastMutex (&(Database->u.FastMutex));
  871. }
  872. Database->Owner = KeGetCurrentThread();
  873. return TRUE;
  874. #else
  875. ASSERT((Database->Flags & RTL_TRACE_IN_USER_MODE));
  876. RtlEnterCriticalSection (&(Database->Lock));
  877. //
  878. // SilviuC: it might be useful to get thread address here
  879. // although not really important.
  880. //
  881. Database->Owner = NULL;
  882. return TRUE;
  883. #endif // #ifdef NTOS_KERNEL_RUNTIME
  884. }
  885. BOOLEAN
  886. RtlpTraceDatabaseReleaseLock (
  887. IN PRTL_TRACE_DATABASE Database
  888. )
  889. /*++
  890. Routine Description:
  891. This routine releases the trace database lock.
  892. It hides all details about the actual nature of the lock.
  893. Arguments:
  894. Database - trace database
  895. Return Value:
  896. TRUE if successful.
  897. Environment:
  898. Internal trace database module function.
  899. --*/
  900. {
  901. #ifdef NTOS_KERNEL_RUNTIME
  902. ASSERT((Database->Flags & RTL_TRACE_IN_KERNEL_MODE));
  903. Database->Owner = NULL;
  904. if ((Database->Flags & RTL_TRACE_USE_NONPAGED_POOL)) {
  905. KeReleaseSpinLock (&(Database->u.SpinLock), Database->SavedIrql);
  906. }
  907. else {
  908. ExReleaseFastMutex (&(Database->u.FastMutex));
  909. }
  910. return TRUE;
  911. #else
  912. ASSERT((Database->Flags & RTL_TRACE_IN_USER_MODE));
  913. Database->Owner = NULL;
  914. RtlLeaveCriticalSection (&(Database->Lock));
  915. return TRUE;
  916. #endif // #ifdef NTOS_KERNEL_RUNTIME
  917. }
  918. PRTL_TRACE_SEGMENT
  919. RtlpTraceSegmentCreate (
  920. IN SIZE_T Size,
  921. IN ULONG Flags, // OPTIONAL in User mode
  922. IN ULONG Tag // OPTIONAL in User mode
  923. )
  924. /*++
  925. Routine Description:
  926. This routine creates a new segment. The segment is the device
  927. through which a database can increase in size to accomodata
  928. more traces.
  929. Arguments:
  930. Size - size in bytes
  931. Flags - allocation flags (U/K mode, P/NP pool)
  932. Tag - tag for K mode allocations
  933. Return Value:
  934. New allocated segment or null.
  935. Environment:
  936. Internal trace database module function.
  937. --*/
  938. {
  939. PRTL_TRACE_SEGMENT Segment;
  940. Segment = RtlpTraceDatabaseAllocate (Size, Flags, Tag);
  941. return Segment;
  942. }
  943. BOOLEAN
  944. RtlTraceDatabaseEnumerate (
  945. PRTL_TRACE_DATABASE Database,
  946. OUT PRTL_TRACE_ENUMERATE Enumerate,
  947. OUT PRTL_TRACE_BLOCK * TraceBlock
  948. )
  949. /*++
  950. Routine Description:
  951. This function enumerates all traces in the database. It requires a
  952. RTL_TRACE_ENUMERATE function (zeroed initially) to keep the state of
  953. the enumeration. Since the trace database does not support delete
  954. operations we do not need to keep a lock across multiple calls to
  955. Enumerate(). However this can change if we add support for deletions.
  956. Arguments:
  957. Database - trace database pointer
  958. Enumerate - enumeration opaque structure. Used to keep the state of
  959. the enumeration.
  960. TraceBlock - on each succesful return this pointer gets filled with
  961. the address of a trace block from the database.
  962. Return Value:
  963. TRUE if a trace block was found (during enumeration) and FALSE if there
  964. are no more blocks in the database.
  965. Environment:
  966. User/Kernel mode.
  967. --*/
  968. {
  969. BOOLEAN Result;
  970. TRACE_ASSERT (Database != NULL);
  971. TRACE_ASSERT (Database->Magic == RTL_TRACE_DATABASE_MAGIC);
  972. //
  973. // (SilviuC): If we ever add support for deleting stack traces
  974. // then it will not be enough to acquire the lock inside the
  975. // call to Enumerate(). We will need to keep the lock across
  976. // calls.
  977. //
  978. RtlpTraceDatabaseAcquireLock (Database);
  979. //
  980. // Start the search process if this is the first call.
  981. // If this is not the first call try to validate what
  982. // we have inside the enumerator.
  983. //
  984. if (Enumerate->Database == NULL) {
  985. Enumerate->Database = Database;
  986. Enumerate->Index = 0;
  987. Enumerate->Block = Database->Buckets[0];
  988. }
  989. else {
  990. if (Enumerate->Database != Database) {
  991. Result = FALSE;
  992. goto Exit;
  993. }
  994. if (Enumerate->Index >= Database->NoOfBuckets) {
  995. Result = FALSE;
  996. goto Exit;
  997. }
  998. }
  999. //
  1000. // Find out the next trace block in case we are at the end
  1001. // of a bucket or the bucket was empty.
  1002. //
  1003. while (Enumerate->Block == NULL) {
  1004. Enumerate->Index += 1;
  1005. if (Enumerate->Index >= Database->NoOfBuckets) {
  1006. break;
  1007. }
  1008. Enumerate->Block = Database->Buckets[Enumerate->Index];
  1009. }
  1010. //
  1011. // Figure out if we have finished the enumeration.
  1012. //
  1013. if (Enumerate->Index >= Database->NoOfBuckets && Enumerate->Block == NULL) {
  1014. *TraceBlock = NULL;
  1015. Result = FALSE;
  1016. goto Exit;
  1017. }
  1018. //
  1019. // Fill out the next trace block and advance the enumerator.
  1020. //
  1021. *TraceBlock = Enumerate->Block;
  1022. Enumerate->Block = Enumerate->Block->Next;
  1023. Result = TRUE;
  1024. //
  1025. // Clean up and exit
  1026. //
  1027. Exit:
  1028. RtlpTraceDatabaseReleaseLock (Database);
  1029. return Result;
  1030. }
  1031. //
  1032. // End of module: tracedb.c
  1033. //