diff --git a/scripts/ebpf_tracing.cmd b/scripts/ebpf_tracing.cmd index f7a2a7a57..6b3ef065f 100644 --- a/scripts/ebpf_tracing.cmd +++ b/scripts/ebpf_tracing.cmd @@ -1,18 +1,6 @@ @rem Copyright (c) Microsoft Corporation @rem SPDX-License-Identifier: MIT -@rem Script behavior: -@rem - When called with 'start', it will: -@rem - Setup the logman session named as defined in 'trace_name', capping circular-log file size to 'max_file_size_mb', and generating every 'rundown_period'. -@rem - Configure the WFP/eBPF events to be monitored -@rem - Start the session within the given 'trace_path' directory. -@rem - When called with 'stop', it will: -@rem - Stop then delete the logman session, and finally deletes the 'trace_path' directory. -@rem - When called with 'periodic', it will: -@rem - Run 'netsh wfp show state' into the 'trace_path' directory, and if the file is under 'max_file_size_mb', it will move it into the '.\committed' subfolder, adding a timestamp to its name. -@rem - Iterate over all the '.etl' files in the 'trace_path' directory, sorted in descending order by "date modified", skip the first 'num_etl_files_to_keep' files and move the others into the '.\committed' subfolder. -@rem - Iterate over all the '.etl' and '.xml' files in the '.\committed' subfolder and delete files older than 'files_max_age_days' days. - @echo off setlocal enabledelayedexpansion @@ -21,7 +9,7 @@ set "command=" set "trace_path=" set "trace_name=ebpf_diag" set "rundown_period=0:35:00" -set "max_file_size_mb=20" +set "max_file_size_mb=30" set "max_committed_folder_size_mb=200" set "max_committed_wfp_state_files=1" @@ -54,7 +42,7 @@ if "%trace_path%" == "" ( ) :run_command -@rem Uncomment ECHOs below for debugging purposes. +@rem Uncomment the ECHOs below for debugging purposes. @rem ---------------------------------------------- @rem echo Running with the following parameter values: @rem echo command=%command% @@ -67,7 +55,7 @@ if "%trace_path%" == "" ( @rem Internal constants set /a num_etl_files_to_keep=1 -set /a max_file_size_bytes=!max_file_size_mb!*1000000 +set /a max_file_size_bytes=!max_file_size_mb!*1024*1024 if not exist "!trace_path!" ( echo Creating trace_path "!trace_path!" @@ -82,44 +70,93 @@ if "%command%"=="periodic" ( mkdir "!traceCommittedPath!" ) + @rem Get the current date and time in a format suitable for appending to file names. + for /f "tokens=2 delims==" %%a in ('wmic OS Get localdatetime /value') do ( + set "dt=%%a" + set "YYYY=!dt:~0,4!" & set "MM=!dt:~4,2!" & set "DD=!dt:~6,2!" + set "HH=!dt:~8,2!" & set "Min=!dt:~10,2!" & set "Sec=!dt:~12,2!" + set "timestamp=!YYYY!!MM!!DD!_!HH!!Min!!Sec!" + ) + @rem Run down the WFP state. pushd "!trace_path!" netsh wfp show state popd - set "wfp_state_file_cab=!trace_path!\wfpstate.cab" - makecab "!trace_path!\wfpstate.xml" "!wfp_state_file_cab!" - if exist "!wfp_state_file_cab!" ( + set "wfp_state_file_xml=!trace_path!\wfpstate.xml" + if exist "!wfp_state_file_xml!" ( @rem If the file size is less or equal than 'max_file_size_mb', then move it to the 'traceCommittedPath' directory. - for %%F in ("!wfp_state_file_cab!") do ( + for %%F in ("!wfp_state_file_xml!") do ( if %%~zF LEQ %max_file_size_bytes% ( - - @rem Get the current date and time in a format suitable for file names. - for /f "tokens=2 delims==" %%a in ('wmic OS Get localdatetime /value') do ( - set "dt=%%a" - set "YYYY=!dt:~0,4!" & set "MM=!dt:~4,2!" & set "DD=!dt:~6,2!" - set "HH=!dt:~8,2!" & set "Min=!dt:~10,2!" & set "Sec=!dt:~12,2!" - set "timestamp=!YYYY!!MM!!DD!_!HH!!Min!!Sec!" - - @rem Move the .CAB file to the 'traceCommittedPath' directory. - move /y "!wfp_state_file_cab!" "!traceCommittedPath!\wfpstate_!timestamp!.cab" >nul - ) + @rem Move the .XML file to the 'traceCommittedPath' directory. + move /y "!wfp_state_file_xml!" "!traceCommittedPath!\wfpstate_!timestamp!.xml" >nul ) else ( - @rem If the .CAB file size is greater than 'max_file_size_mb', then delete it. - del "!wfp_state_file_cab!" + @rem If the .XML file size is greater than 'max_file_size_mb', then delete it. + del "!wfp_state_file_xml!" ) ) ) + @rem Run down the program state using bpftool + pushd "!trace_path!" + @rem Capture program output + echo bpftool.exe -p prog >> bpf_state.txt + bpftool.exe -p prog >> bpf_state.txt + @rem Capture link output + echo bpftool.exe -p link >> bpf_state.txt + bpftool.exe -p link >> bpf_state.txt + @rem Capture map output + echo bpftool.exe -p map >> bpf_state.txt + bpftool.exe -p map >> bpf_state.txt + @rem Capture map content output. This requires the map id value to be passed in. + @rem This script parses the 'Bpftool.exe map' output to extract the map ids to be passed into the 'bpftool.exe map dump' command + @rem Store 'bpftool.exe -j map' output + for /F "usebackq" %%A in (`bpftool.exe -j map`) do set "jsonString=%%A" + @rem Clean the output to parse it. + @rem Remove the outer brackets '[' and ']' + set "jsonString=!jsonString:~1,-1!" + @rem Remove other characters from the output: '{', '}', and '"' + set "jsonString=!jsonString:{=%!" + set "jsonString=!jsonString:}=%!" + set "jsonString=!jsonString:"=%!" + @rem Split the string into key,value pairs + for %%A in ("!jsonString:,=" "!") do ( + set "jsonKeyValue=%%~A" + @rem Split each pair into separate key and value variables + for /F "tokens=1,2 delims=:" %%B in ("!jsonKeyValue!") do ( + set "key=%%B" + set "value=%%C" + ) + @rem If the 'key' is 'id', then use the 'value' (id value) to capture the output of 'bpftool.exe map dump' + if "!key!"=="id" ( + echo bpftool.exe map dump id !value! >> bpf_state.txt + bpftool.exe map dump id !value! >> bpf_state.txt + ) + ) + set "bpf_state_file=!trace_path!\bpf_state.txt" + if exist "!bpf_state_file!" ( + @rem If the file size is less or equal than 'max_file_size_mb', then move it to the 'traceCommittedPath' directory. + for %%F in ("!bpf_state_file!") do ( + if %%~zF LEQ %max_file_size_bytes% ( + @rem Move the file to the 'traceCommittedPath' directory. + move /y "!bpf_state_file!" "!traceCommittedPath!\bpfstate_!timestamp!.txt" >nul + ) else ( + @rem If the file size is greater than 'max_file_size_mb', then delete it. + del "!bpf_state_file!" + ) + ) + ) + popd + @rem Iterate over all the .etl files in the 'trace_path' directory, sorted in descending order by name, @rem and skip the first 'num_etl_files_to_keep' files (i.e., the newest 'num_etl_files_to_keep' files). for /f "skip=%num_etl_files_to_keep% delims=" %%f in ('dir /b /o-n "!trace_path!\*.etl"') do ( move /y "!trace_path!\%%f" "!traceCommittedPath!" >nul ) - @rem Iterate over all the WFP-state files in the 'traceCommittedPath' directory, and delete files overflowing `max_committed_wfp_state_files`. - for /f "skip=%max_committed_wfp_state_files% delims=" %%f in ('dir /b /o-d "!traceCommittedPath!\wfpstate*.cab"') do ( del "!traceCommittedPath!\%%f" ) + @rem Iterate over all the WFP-state files in the 'traceCommittedPath' directory, and delete files overflowing `max_committed_rundown_state_files`. + for /f "skip=%max_committed_rundown_state_files% delims=" %%f in ('dir /b /o-d "!traceCommittedPath!\wfpstate*.xml"') do ( del "!traceCommittedPath!\%%f" ) @rem Iterate over all the .ETL files in the 'traceCommittedPath' directory, and delete the older files overflowing `max_committed_folder_size_mb`. set size=0 @@ -170,18 +207,30 @@ echo Usage: ebpf_tracing.cmd command /trace_path path [/trace_name name] [/rundo echo: echo Valid parameters: echo: -echo - (mandatory) Valid values are: [start, stop, periodic] -echo /trace_path path - (mandatory) Path into which the tracing will be located (creates it if it does not exist). -echo /trace_name name - Name of the logman trace (Default: "ebpf_diag") -echo /rundown_period period - Period, expressed as (H:mm:ss), for saving and generating a new ETL log, and for generating a WFP state snapshot (Default: 0:35:00). -echo /max_file_size_mb size - Maximum size set for an ETL log (Default: 20). -echo /max_committed_folder_size_mb count - Maximum overall size for (most recent) .ETL files to keep in the main 'trace_path\committed' (Default: 200) -echo /max_committed_wfp_state_files count - Number of (most recent) WFP-state .CAB files to keep in the main 'trace_path\committed' (Default: 1). +echo - (mandatory) Valid values are: [start, stop, periodic] +echo /trace_path path - (mandatory) Path into which the tracing will be located (creates it if it does not exist). +echo /trace_name name - Name of the logman trace (Default: "ebpf_diag") +echo /rundown_period period - Period, expressed as (H:mm:ss), for saving and generating a new ETL log, and for generating a WFP state snapshot (Default: 0:35:00). +echo /max_file_size_mb size - Maximum size set for an ETL log (Default: 20). +echo /max_committed_folder_size_mb size - Maximum overall size for (most recent) .ETL files to keep in the main 'trace_path\committed' (Default: 200) +echo /max_committed_rundown_state_files count - Number (most recent) of each type of rundown state file to keep in the main 'trace_path\committed' (Default: 1). echo: -echo Examples (overriding defaults): +echo Behaviour: +echo - When called with the 'start' command, it will: +echo - Set up the logman session named as defined in 'trace_name', capping circular-log file size to 'max_file_size_mb', and generating every 'rundown_period'. +echo - Configure the WFP/eBPF events to be monitored +echo - Start the session within the given 'trace_path' directory. +echo - When called with the 'stop' command, it will: +echo - Stop then delete the logman session, and delete the 'trace_path' directory. +echo - When called with the 'periodic' command, it will: +echo - Run 'netsh wfp show state' into the 'trace_path' directory, and if the file is under 'max_file_size_mb', it will move it into the 'trace_path\committed' subfolder, adding a timestamp to its name. +echo - Run down the program state using bpftool, to capture the program output: link, map, and map content outputs, and store them in "bpf_state.txt". Like done for the WFP state, if the file is under 'max_file_size_mb', it will move it into the 'trace_path\committed' subfolder, adding a timestamp to its name. +echo - Iterate over all the '.xml' files in the 'trace_path\committed' subfolder and delete the older files overflowing 'max_committed_rundown_state_files'. +echo - Iterate over all the '.etl' files in the 'trace_path' directory, sorted in descending order by 'date modified', skip the first files summing up to 'max_committed_folder_size_mb' and move the others into the 'trace_path\committed' subfolder. echo: -echo ebpf_tracing.cmd start /trace_name ebpf_diag /trace_path "%SystemRoot%\Logs\eBPF" /rundown_period 0:35:00 /max_file_size_mb 20 -echo ebpf_tracing.cmd stop /trace_name ebpf_diag /trace_path "%SystemRoot%\Logs\eBPF" -echo ebpf_tracing.cmd periodic /trace_path "%SystemRoot%\Logs\eBPF" /max_file_size_mb 20 /max_committed_folder_size_mb 30 /max_committed_wfp_state_files 1 +echo Examples: +echo ebpf_tracing.cmd start /trace_name ebpf_diag /trace_path "%SystemRoot%\Logs\eBPF" /rundown_period 0:35:00 /max_file_size_mb 20 +echo ebpf_tracing.cmd stop /trace_name ebpf_diag /trace_path "%SystemRoot%\Logs\eBPF" +echo ebpf_tracing.cmd periodic /trace_path "%SystemRoot%\Logs\eBPF" /max_file_size_mb 20 /max_committed_folder_size_mb 30 /max_committed_rundown_state_files 1 endlocal exit /b 1 diff --git a/scripts/ebpf_tracing_periodic_task.xml b/scripts/ebpf_tracing_periodic_task.xml index 149ba677d..c91b28fa8 100644 Binary files a/scripts/ebpf_tracing_periodic_task.xml and b/scripts/ebpf_tracing_periodic_task.xml differ