Skip to content

Commit 1c8058d

Browse files
committed
Implement new AtExitEx functionality; make PythonTracer use it to save max ref counts on exit.
1 parent f820fd8 commit 1c8058d

21 files changed

+1118
-161
lines changed

PythonTracer/PythonTracer.c

Lines changed: 107 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -389,8 +389,8 @@ InitializePythonTraceContext(
389389
PVOID UserData
390390
)
391391
{
392+
BOOL Success;
392393
PTRACE_STORES TraceStores;
393-
PTRACE_STORE EventStore;
394394
PTRACE_STORE StringBufferStore;
395395
PTRACE_STORE FunctionTableStore;
396396
PTRACE_STORE FunctionTableEntryStore;
@@ -401,7 +401,11 @@ InitializePythonTraceContext(
401401
PPY_TRACE_CALLBACK Callback;
402402
PYTHON_ALLOCATORS Allocators;
403403
ULONG NumberOfAllocators = 0;
404-
USHORT TraceStoreIndex;
404+
TRACE_STORE_ID TraceStoreId;
405+
406+
//
407+
// Validate arguments.
408+
//
405409

406410
if (!Context) {
407411
if (SizeOfContext) {
@@ -440,6 +444,10 @@ InitializePythonTraceContext(
440444
return FALSE;
441445
}
442446

447+
//
448+
// Arguments are valid, continue with initialization.
449+
//
450+
443451
SecureZeroMemory(Context, sizeof(*Context));
444452

445453
Context->Size = *SizeOfContext;
@@ -456,9 +464,8 @@ InitializePythonTraceContext(
456464
TraceStores = TraceContext->TraceStores;
457465

458466
#define INIT_STORE_ALLOCATOR(Name) \
459-
TraceStoreIndex = TraceStore##Name##Index; \
460-
Name##Store = &TraceStores->Stores[TraceStore##Name##Index]; \
461-
Name##Store->NoRetire = TRUE; \
467+
TraceStoreId = TraceStore##Name##Id; \
468+
Name##Store = TraceStoreIdToTraceStore(TraceStores, TraceStoreId); \
462469
Allocators.##Name##.AllocationRoutine = TraceStoreAllocationRoutine; \
463470
Allocators.##Name##.AllocationContext = ##Name##Store; \
464471
Allocators.##Name##.FreeRoutine = TraceStoreFreeRoutine; \
@@ -493,13 +500,6 @@ InitializePythonTraceContext(
493500
INIT_STORE_ALLOCATOR(StringArray);
494501
INIT_STORE_ALLOCATOR(StringTable);
495502

496-
EventStore = &TraceStores->Stores[TraceStoreEventIndex];
497-
498-
if (EventStore->NoRetire) {
499-
__debugbreak();
500-
return FALSE;
501-
}
502-
503503
Allocators.NumberOfAllocators = NumberOfAllocators;
504504
Allocators.SizeInBytes = sizeof(Allocators);
505505

@@ -528,9 +528,104 @@ InitializePythonTraceContext(
528528
Context->AddModuleName = AddModuleName;
529529
Context->SetModuleNamesStringTable = SetModuleNamesStringTable;
530530

531+
//
532+
// If we've been configured to track maximum reference counts, register an
533+
// extended atexit function that will reflect the maximum values we saw
534+
// in the registry on exit (if they exceed previously set maximums).
535+
//
536+
// (This is also a good test that our SetAtExitEx() machinery is working
537+
// properly, which is why we call the global version instead of going
538+
// through Rtl->RegisterAtExitEx().)
539+
//
540+
// N.B. Slight quirk: we can't use Context->Flags yet as they won't be set
541+
// correctly until after this call returns to TracedPythonSession, so,
542+
// we have to use to config's flags.
543+
//
544+
545+
if (TraceContext->TracerConfig->Flags.TrackMaxRefCounts) {
546+
Success = AtExitEx(SaveMaxRefCountsAtExit,
547+
NULL,
548+
Context,
549+
&Context->AtExitEntry);
550+
if (!Success) {
551+
return FALSE;
552+
}
553+
}
554+
531555
return TRUE;
532556
}
533557

558+
_Use_decl_annotations_
559+
VOID
560+
SaveMaxRefCountsAtExit(
561+
BOOL IsProcessTerminating,
562+
PPYTHON_TRACE_CONTEXT Context
563+
)
564+
{
565+
ULONG Result;
566+
ULONG Disposition;
567+
HKEY RegistryKey;
568+
CONST UNICODE_STRING RegistryPath = \
569+
RTL_CONSTANT_STRING(L"Software\\PythonTracer");
570+
571+
//
572+
// Validate arguments.
573+
//
574+
575+
if (!ARGUMENT_PRESENT(Context)) {
576+
return;
577+
}
578+
579+
//
580+
// Arguments are valid, open or create the registry path.
581+
//
582+
583+
Result = RegCreateKeyExW(
584+
HKEY_CURRENT_USER,
585+
RegistryPath.Buffer,
586+
0, // Reserved
587+
NULL, // Class
588+
REG_OPTION_NON_VOLATILE,
589+
KEY_ALL_ACCESS,
590+
NULL,
591+
&RegistryKey,
592+
&Disposition
593+
);
594+
595+
if (Result != ERROR_SUCCESS) {
596+
OutputDebugStringA("RegCreateKeyExW() failed for: ");
597+
OutputDebugStringW(RegistryPath.Buffer);
598+
return;
599+
}
600+
601+
//
602+
// Write the max ref counts to the registry if they're greater than the
603+
// current version.
604+
//
605+
606+
#define UPDATE_COUNT(Name) \
607+
UPDATE_MAX_REG_QWORD(RegistryKey, \
608+
Name, \
609+
(ULONGLONG)Context->##Name##.QuadPart)
610+
611+
UPDATE_COUNT(MaxNoneRefCount);
612+
UPDATE_COUNT(MaxTrueRefCount);
613+
UPDATE_COUNT(MaxZeroRefCount);
614+
UPDATE_COUNT(MaxFalseRefCount);
615+
616+
//
617+
// Capture MaxDepth as well.
618+
//
619+
620+
UPDATE_COUNT(MaxDepth);
621+
622+
//
623+
// Close the registry key.
624+
//
625+
626+
RegCloseKey(RegistryKey);
627+
}
628+
534629
_Use_decl_annotations_
535630
BOOL
536631
Start(

PythonTracer/PythonTracer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,8 @@ typedef struct _Struct_size_bytes_(Size) _PYTHON_TRACE_CONTEXT {
332332
PADD_MODULE_NAME AddModuleName;
333333
PSET_MODULE_NAMES_STRING_TABLE SetModuleNamesStringTable;
334334

335+
struct _RTL_ATEXIT_ENTRY *AtExitEntry;
336+
335337
} PYTHON_TRACE_CONTEXT, *PPYTHON_TRACE_CONTEXT;
336338

337339
typedef

PythonTracer/PythonTracer.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,15 @@
9797
</ClCompile>
9898
</ItemDefinitionGroup>
9999
<ItemGroup>
100+
<ClInclude Include="..\Rtl\AtExitEx.h" />
100101
<ClInclude Include="PythonTracerConstants.h" />
101102
<ClInclude Include="stdafx.h" />
102103
<ClInclude Include="targetver.h" />
103104
<ClInclude Include="PythonTracer.h" />
104105
<ClInclude Include="PythonTracerPrivate.h" />
105106
</ItemGroup>
106107
<ItemGroup>
108+
<ClCompile Include="..\Rtl\AtExitEx.c" />
107109
<ClCompile Include="..\Rtl\__C_specific_handler.c" />
108110
<ClCompile Include="dllmain.c">
109111
<PrecompiledHeader />

PythonTracer/PythonTracer.vcxproj.filters

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
<ClInclude Include="PythonTracerConstants.h">
3131
<Filter>Header Files</Filter>
3232
</ClInclude>
33+
<ClInclude Include="..\Rtl\AtExitEx.h">
34+
<Filter>Header Files</Filter>
35+
</ClInclude>
3336
</ItemGroup>
3437
<ItemGroup>
3538
<ClCompile Include="stdafx.c">
@@ -62,5 +65,8 @@
6265
<ClCompile Include="PyTraceEvent4.c">
6366
<Filter>Source Files</Filter>
6467
</ClCompile>
68+
<ClCompile Include="..\Rtl\AtExitEx.c">
69+
<Filter>Source Files</Filter>
70+
</ClCompile>
6571
</ItemGroup>
6672
</Project>

PythonTracer/PythonTracerPrivate.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ Module Name:
1515
1616
--*/
1717

18+
#ifndef _PYTHON_TRACER_INTERNAL_BUILD
19+
#error PythonTracerPrivate.h being included but _PYTHON_TRACER_INTERNAL_BUILD
20+
#error not set.
21+
#endif
22+
1823
#pragma once
1924

2025
#include "stdafx.h"
@@ -23,6 +28,16 @@ Module Name:
2328
extern "C" {
2429
#endif
2530

31+
//
32+
// AtExitEx-related definitions.
33+
//
34+
35+
ATEXITEX_CALLBACK SaveMaxRefCountsAtExit;
36+
37+
////////////////////////////////////////////////////////////////////////////////
38+
// Inline functions.
39+
////////////////////////////////////////////////////////////////////////////////
40+
2641
FORCEINLINE
2742
BOOL
2843
IsFunctionOfInterestStringTable(

PythonTracer/stdafx.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@ Module Name:
1919
#include <Windows.h>
2020
#include "../Rtl/Rtl.h"
2121
#include "../Rtl/__C_specific_handler.h"
22+
#include "../Rtl/AtExitEx.h"
2223
#include "../Python/Python.h"
2324
#include "../TraceStore/TraceStore.h"
2425
#include "../StringTable/StringTable.h"
2526
#include "PythonTracer.h"
2627
#include "PythonTracerConstants.h"
28+
29+
#ifdef _PYTHON_TRACER_INTERNAL_BUILD
2730
#include "PythonTracerPrivate.h"
31+
#endif
2832

2933
// vim:set ts=8 sw=4 sts=4 tw=80 expandtab :

Rtl/AtExitEx.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*++
2+
3+
Copyright (c) 2016 Trent Nelson <[email protected]>
4+
5+
Module Name:
6+
7+
AtExitEx.c
8+
9+
Abstract:
10+
11+
This module implements the SetAtExitEx() and AtExitEx() stubs.
12+
13+
--*/
14+
15+
#include "stdafx.h"
16+
17+
PATEXITEX AtExitExImpl = NULL;
18+
19+
VOID
20+
SetAtExitEx(
21+
PATEXITEX AtExitEx
22+
)
23+
{
24+
AtExitExImpl = AtExitEx;
25+
}
26+
27+
#pragma warning(push)
28+
#pragma warning(disable: 4028 4273)
29+
30+
_Use_decl_annotations_
31+
BOOL
32+
AtExitEx(
33+
PATEXITEX_CALLBACK Callback,
34+
PATEXITEX_FLAGS Flags,
35+
PVOID Context,
36+
struct _RTL_ATEXIT_ENTRY **EntryPointer
37+
)
38+
{
39+
return AtExitExImpl(Callback, Flags, Context, EntryPointer);
40+
}
41+
42+
#pragma warning(pop)
43+
44+
// vim:set ts=8 sw=4 sts=4 tw=80 expandtab :

Rtl/AtExitEx.h

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*++
2+
3+
Copyright (c) 2016 Trent Nelson <[email protected]>
4+
5+
Module Name:
6+
7+
AtExitEx.h
8+
9+
Abstract:
10+
11+
This module provides the necessary scaffolding to use the AtExitEx()
12+
function without needing to have any knowledge of the Rtl library.
13+
14+
To make use of this functionality, add this file and AtExitEx.c to your
15+
project. For components loaded via the TracedPythonSession component,
16+
SetAtExitEx() will be called automatically as soon as the component has
17+
been dynamically loaded (via LoadLibrary()).
18+
19+
--*/
20+
21+
#ifdef _NO_RTL
22+
23+
typedef struct _Struct_size_bytes_(sizeof(ULONG)) _ATEXITEX_FLAGS {
24+
ULONG SuppressExceptions:1;
25+
} ATEXITEX_FLAGS, *PATEXITEX_FLAGS;
26+
C_ASSERT(sizeof(ATEXITEX_FLAGS) == sizeof(ULONG));
27+
28+
typedef
29+
VOID
30+
(CALLBACK ATEXITEX_CALLBACK)(
31+
_In_ BOOL IsProcessTerminating,
32+
_In_opt_ PVOID Context
33+
);
34+
typedef ATEXITEX_CALLBACK *PATEXITEX_CALLBACK;
35+
36+
typedef
37+
_Success_(return == 0)
38+
BOOL
39+
(ATEXITEX)(
40+
_In_ PATEXITEX_CALLBACK Callback,
41+
_In_opt_ PATEXITEX_FLAGS Flags,
42+
_In_opt_ PVOID Context,
43+
_Outptr_opt_result_nullonfailure_ struct _RTL_ATEXIT_ENTRY **EntryPointer
44+
);
45+
typedef ATEXITEX *PATEXITEX;
46+
47+
typedef
48+
VOID
49+
(SET_ATEXITEX)(
50+
_In_ PATEXITEX AtExitEx
51+
);
52+
typedef SET_ATEXITEX *PSET_ATEXITEX;
53+
54+
#endif
55+
56+
extern PATEXITEX AtExitExImpl;
57+
__declspec(dllexport) SET_ATEXITEX SetAtExitEx;
58+
59+
#pragma warning(push)
60+
#pragma warning(disable: 4028 4273)
61+
62+
ATEXITEX AtExitEx;
63+
64+
#pragma warning(pop)
65+
66+
// vim:set ts=8 sw=4 sts=4 tw=80 expandtab :

Rtl/Path.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ Return Value:
610610
}
611611

612612
AllocSize.LongPart = (
613-
(NumberOfChars.LongPart << 1) +
613+
(NumberOfChars.LowPart << 1) +
614614
sizeof(UNICODE_STRING)
615615
);
616616
AlignedAllocSize.LongPart = ALIGN_UP_POINTER(AllocSize.LongPart);

0 commit comments

Comments
 (0)