Skip to content

Commit 1ab92db

Browse files
authored
Implement /RundownMaxMB using an ETW-based file-size limitation. (#2148)
This ensures that if PerfView crashes or is killed, the file size limitation will still be respected.
1 parent bb883bc commit 1ab92db

File tree

2 files changed

+69
-11
lines changed

2 files changed

+69
-11
lines changed

src/PerfView/CommandProcessor.cs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3431,6 +3431,11 @@ private void DoClrRundownForSession(string fileName, string sessionName, Command
34313431
using (TraceEventSession clrRundownSession = new TraceEventSession(sessionName + "Rundown", rundownFile))
34323432
{
34333433
clrRundownSession.BufferSizeMB = Math.Max(parsedArgs.BufferSizeMB, 256);
3434+
if (parsedArgs.RundownMaxMB > 0)
3435+
{
3436+
LogFile.WriteLine($"Maximum rundown file size is {parsedArgs.RundownMaxMB}MB. Use /RundownMaxMB to change.");
3437+
clrRundownSession.MaximumFileMB = parsedArgs.RundownMaxMB;
3438+
}
34343439

34353440
TraceEventProviderOptions options = null;
34363441
if (parsedArgs.FocusProcess != null && TraceEventProviderOptions.FilteringSupported)
@@ -3561,7 +3566,7 @@ private void DoClrRundownForSession(string fileName, string sessionName, Command
35613566
PerfViewLogger.Log.WaitForIdle();
35623567

35633568
// Wait for rundown to complete.
3564-
WaitForRundownIdle(parsedArgs.MinRundownTime, parsedArgs.RundownTimeout, parsedArgs.RundownMaxMB, rundownFile);
3569+
WaitForRundownIdle(parsedArgs.MinRundownTime, parsedArgs.RundownTimeout, rundownFile);
35653570

35663571
// Complete perfview rundown.
35673572
DotNetVersionLogger.Stop();
@@ -3587,10 +3592,9 @@ private void DoClrRundownForSession(string fileName, string sessionName, Command
35873592
/// Currently there is no good way to know when rundown is finished. We basically wait as long as
35883593
/// the rundown file is growing.
35893594
/// </summary>
3590-
private void WaitForRundownIdle(int minSeconds, int maxSeconds, int maxSizeMB, string rundownFilePath)
3595+
private void WaitForRundownIdle(int minSeconds, int maxSeconds, string rundownFilePath)
35913596
{
35923597
LogFile.WriteLine("Waiting up to {0} sec for rundown events. Use /RundownTimeout to change.", maxSeconds);
3593-
LogFile.WriteLine($"Maximum rundown file size is {maxSizeMB}MB. Use /RundownMaxMB to change.");
35943598
LogFile.WriteLine("If you know your process has exited, use /noRundown qualifer to skip this step.");
35953599

35963600
long rundownFileLen = 0;
@@ -3607,12 +3611,6 @@ private void WaitForRundownIdle(int minSeconds, int maxSeconds, int maxSizeMB, s
36073611
LogFile.WriteLine("Rundown File Length: {0:n1}MB delta: {1:n1}MB", newRundownFileLen / 1000000.0, delta / 1000000.0);
36083612
rundownFileLen = newRundownFileLen;
36093613

3610-
if ((maxSizeMB > 0) && rundownFileLen >= ((long)maxSizeMB * 1024 * 1024))
3611-
{
3612-
LogFile.WriteLine($"Exceeded maximum rundown file size of {maxSizeMB}MB.");
3613-
break;
3614-
}
3615-
36163614
if (i >= minSeconds)
36173615
{
36183616
if (delta == 0 && newRundownFileLen != 0)

src/TraceEvent/TraceEventSession.cs

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ public TraceEventSession(string sessionName, TraceEventSessionOptions options =
138138
m_FileName = null;
139139
m_CircularBufferMB = m_BufferSizeMB;
140140
}
141+
if ((properties->LogFileMode & TraceEventNativeMethods.EVENT_TRACE_FILE_MODE_SEQUENTIAL) != 0)
142+
{
143+
m_MaximumFileMB = (int)properties->MaximumFileSize;
144+
}
141145
if ((properties->LogFileMode & TraceEventNativeMethods.EVENT_TRACE_NO_PER_PROCESSOR_BUFFERING) != 0)
142146
{
143147
m_NoPerProcessBuffering = true;
@@ -1164,7 +1168,12 @@ public int CircularBufferMB
11641168

11651169
if (m_MultiFileMB != 0)
11661170
{
1167-
throw new InvalidOperationException("Cannot specify both CircularBufferMB and MultiFileMB.");
1171+
throw new InvalidOperationException("Cannot specify more than one of CircularBufferMB, MultiFileMB, and MaximumFileMB.");
1172+
}
1173+
1174+
if (m_MaximumFileMB != 0)
1175+
{
1176+
throw new InvalidOperationException("Cannot specify more than one of CircularBufferMB, MultiFileMB, and MaximumFileMB.");
11681177
}
11691178

11701179
m_CircularBufferMB = value;
@@ -1197,7 +1206,12 @@ public int MultiFileMB
11971206

11981207
if (m_CircularBufferMB != 0)
11991208
{
1200-
throw new InvalidOperationException("Cannot specify both CircularBufferMB and MultiFileMB.");
1209+
throw new InvalidOperationException("Cannot specify more than one of CircularBufferMB, MultiFileMB, and MaximumFileMB.");
1210+
}
1211+
1212+
if (m_MaximumFileMB != 0)
1213+
{
1214+
throw new InvalidOperationException("Cannot specify more than one of CircularBufferMB, MultiFileMB, and MaximumFileMB.");
12011215
}
12021216

12031217
if (!m_FileName.EndsWith(".etl", StringComparison.OrdinalIgnoreCase))
@@ -1222,6 +1236,50 @@ public int MultiFileMB
12221236
m_MultiFileMB = value;
12231237
}
12241238
}
1239+
1240+
/// <summary>
1241+
/// Cause the as a set of files with a given maximum size. The file name must end in .ETL and the
1242+
/// output is then a series of files of the form *NNN.ETL (That is it adds a number just before the
1243+
/// .etl suffix). If you make your file name *.user.etl then the output will be *.user1.etl, *.user2.etl ...
1244+
/// And the MergeInPlace command below will merge them all nicely.
1245+
///
1246+
/// You can have more control over this by using a normal sequential file but use the SetFileName()
1247+
/// method to redirect the data to new files as needed.
1248+
/// </summary>
1249+
public int MaximumFileMB
1250+
{
1251+
get { return m_MaximumFileMB; }
1252+
set
1253+
{
1254+
if (IsActive)
1255+
{
1256+
throw new InvalidOperationException("Property can't be changed after a provider has started.");
1257+
}
1258+
1259+
if (m_FileName == null)
1260+
{
1261+
throw new InvalidOperationException("MultiFile is only allowed on sessions with files.");
1262+
}
1263+
1264+
if (m_CircularBufferMB != 0)
1265+
{
1266+
throw new InvalidOperationException("Cannot specify more than one of CircularBufferMB, MultiFileMB, and MaximumFileMB.");
1267+
}
1268+
1269+
if (m_MultiFileMB != 0)
1270+
{
1271+
throw new InvalidOperationException("Cannot specify more than one of CircularBufferMB, MultiFileMB, and MaximumFileMB.");
1272+
}
1273+
1274+
if (!m_FileName.EndsWith(".etl", StringComparison.OrdinalIgnoreCase))
1275+
{
1276+
throw new InvalidOperationException("FileName must have .etl suffix");
1277+
}
1278+
1279+
m_MaximumFileMB = value;
1280+
}
1281+
}
1282+
12251283
/// <summary>
12261284
/// Sets the size of the buffer the operating system should reserve to avoid lost packets. Starts out
12271285
/// as a very generous 64MB for files. If events are lost, this can be increased, but keep in mind that
@@ -2400,6 +2458,7 @@ private void EnsureStarted(TraceEventNativeMethods.EVENT_TRACE_PROPERTIES* prope
24002458
else
24012459
{
24022460
properties->LogFileMode |= TraceEventNativeMethods.EVENT_TRACE_FILE_MODE_SEQUENTIAL;
2461+
properties->MaximumFileSize = (uint)m_MaximumFileMB;
24032462
}
24042463

24052464
if (m_FileName.Length > MaxNameSize - 1)
@@ -2557,6 +2616,7 @@ public static int GetMaxLastBranchRecordingSources()
25572616
private int m_BufferQuantumKB;
25582617
private int m_CircularBufferMB;
25592618
private int m_MultiFileMB;
2619+
private int m_MaximumFileMB;
25602620
private bool m_IsPrivateLogger;
25612621
private bool m_IsPrivateInProcLogger;
25622622

0 commit comments

Comments
 (0)