VMSTAT 分析器






3.69/5 (9投票s)
帮助 UNIX / Linux 管理员分析 VMSTAT 文件以了解系统资源利用率。
引言
如果您使用 VMSTAT 日志文件管理任何 Linux/UNIX 服务器,并希望有一个工具或应用程序能够通过显示可定制的图表以及每个描述符中每个组件的统计数据来帮助您分析日志文件,那么您来对地方了。继续阅读文章,下载应用程序并使用它。
此应用程序将演示如何有效使用 VMSTAT 分析器。该应用程序具有足够的用户配置灵活性。用户需要提供或找到 s/he 想要分析的 VMSTAT 文件,以了解 UNIX / Linux 系统资源利用率。
下面列出了该应用程序中可以通过用户友好界面轻松执行的一些功能
- 一旦用户在应用程序中加载 VMSTAT 日志文件,它会检查日志文件的完整性和有效性,并在出现任何警告或错误时提示用户。所以,您可以坐下来放松,因为 VMSTAT_Analyzer.exe 会检查 VMSTAT 日志文件的完整性和有效性。
- 用户可以查看进程、内存、交换、IO、系统、CPU(作为字段描述符)的服务器资源利用率,并且可自定义图表中还有单独的组件以及 VMSTAT 日志文件中包含的其他统计数据。
- 此数据从用户已加载到 VMSTAT_Analyzer.exe 中的 VMSTAT 文件中解析,并动态显示数据。
- 它还帮助用户了解每个字段描述符中每个组件的含义。
- 它帮助用户了解日志文件中每个字段描述符中每个组件的最小值、平均值、最大值、百分位数和标准差值(作为统计数据)。
- 用户可以选择图表的粒度/缩放,通过在图表内左键拖动鼠标光标,将图表细分为小部分。
- 用户可以通过右键单击菜单并选择选项,取消缩放或撤消用户在图表上进行的所有缩放,以全尺寸显示。
- X 轴根据数据点数量计算,并转换为 HH:MM:SS 格式,这有助于用户了解 VMSTAT 在服务器中运行了多长时间。
- 该应用程序具有足够的用户配置灵活性。
- 用户可以根据自己的选择更改图表的标题、X 轴和 Y 轴,这将覆盖默认值。
- 用户可以更改日志文件中可用数据点的时间间隔。此时间间隔值完全取决于用户在 VMSTAT 命令中提及的值,并且非常重要进行配置,因为 X 轴完全取决于此时间间隔值和 VMSTAT 日志文件中可用数据点的数量。例如,如果用户使用 vmstat -n 1 > vmstat.log 从服务器收集 VMSTAT 日志文件,则将时间间隔设置为 1。如果用户使用 vmstat -n 5 > vmstat.log,则将时间间隔设置为 5。此值以秒为单位。
- 用户可以更改统计数据的百分位数。此值以 % 为单位。
- 用户可以选择更改图表的线条颜色,这有助于提高可见性。
- 用户可以将图表保存为各种图像格式到所需位置或剪贴板。这可以通过右键单击图表并选择菜单来轻松完成。
- 用户可以将统计值以 CSV 格式导出到所需位置。这可以通过右键单击“统计”部分并选择菜单来轻松完成。
每个人在 UNIX / Linux 环境中创建 VMSTAT 文件时都应该考虑的最重要一点:请使用 vmstat -n 1 > vmstat.log。您可以重定向到任何您想要的文件名和任何 VMSTAT 持续时间(数据点的时间间隔)。但请注意选项 -n。我不会描述任何关于 VMSTAT 的内容(这超出了本文的范围),但请记住,-n 会在 VMSTAT 日志文件中获取所需的标题,通过该标题,VMSTAT_Analyzer.exe 可以理解并显示可自定义的信息。
背景
几天来,我一直在互联网上搜索类似于本文应用程序的东西,它将帮助我显示每个组件的图表,并提供可配置的统计值。但我什么也没找到。
就在那时,我决定创建一个并将其发布到 www.codeproject.com,以便其他人可以使用它并从中获得帮助。
屏幕截图
加载使用命令 vmstat -n 1 > vmstat.log 在服务器中创建的有效 VMSTAT 日志文件后,显示可定制的图表和统计数据。
显示用户选择查看哪些字段描述符的数据。
显示用户选择配置图表和各种其他因素
用户可以更改线条颜色,这会影响图表绘制以提高可见性
使用代码
我遵循了一个非常简单的代码,代码中没有任何复杂之处。如果您下载源代码文件,任何人都可以掌握代码。您可以随意修改代码。但如果您能与他人分享,那就太好了。
让我简要介绍一下这段代码片段。它相当简单。如您所见,VMSTAT 日志文件有一个特定的标题来分类各种服务器利用率矩阵,当我们在 UNIX / Linux 服务器中使用命令 vmstat -n 1 > vmstat.log 创建日志文件时,我们就会得到这个标题。因此,VMSTAT_Analyzer.exe 将做的第一件事是检查用户在应用程序中加载的 VMSTAT 日志文件的完整性和有效性。下面是此代码片段将执行的一些过程。
- 计算 VMSTAT 日志文件中可用数据点的数量,以确定图表中 X 轴的值。它执行 [-2] 以减少标题字符串的数据点。
- 它定义了两个变量,用于存储标题字符串,当用户在服务器中使用命令 vmstat -n 1 > vmstat.log 时,该字符串是常量。
- 然后它从 VMSTAT 日志文件中读取每一行,并用空格分隔/拆分该行,以便可以从每一行读取每个子字符串。
- 如果从日志文件中读取的行是由变量
intNumberOfDataPoint
= 0 确定的第一行,则将每个子字符串与变量strFirstLineVMSTATArr
进行比较。这将有助于验证 VMSTAT 日志中的第一行。 - 对第二行执行相同的操作,并与变量
strSecondLineVMSTATArr
进行比较,这将验证 VMSTAT 日志中的第二行。 - 从第三行到日志文件末尾,它应该包含各个组件的值。因此,读取每一行,并查看这些值是否为整数值,并且每一行应该恰好包含 16 个值。
如果上述任何条件不匹配,则抛出错误并告知用户 VMSTAT 日志文件的哪一行有错误以及错误是什么,以便用户了解错误。
//The very first thing this application does
//is to check for Validity and Integrity of VMSTAT File.
//The VMSTAT file should be created using the command
//vmstat -n 1 > vmstat.log; in you LINUX / UNIX Server
private void CheckIntegrity()
{
int intNumberOfDataPoint = 0;
intGlobalTotalRowsInVMSTATFile = 0;
string strVMSTATLineForTotalRows = null;
try
{
StreamReader readerTotalRows = File.OpenText(strGlobalVMSTATFile);
while ((strVMSTATLineForTotalRows = readerTotalRows.ReadLine()) != null)
{
intGlobalTotalRowsInVMSTATFile = intGlobalTotalRowsInVMSTATFile + 1;
}
readerTotalRows.Close();
stringArrayGlobalDataPointForX =
new string[intGlobalTotalRowsInVMSTATFile - 2];
StreamReader reader = File.OpenText(strGlobalVMSTATFile);
//First line of VMSTAT report
string[] strFirstLineVMSTATArr = { "procs",
"-----------memory----------",
"---swap--", "-----io----",
"--system--", "----cpu----" };
//Second line of VMSTAT report
string[] strSecondLineVMSTATArr = { "r", "b", "swpd",
"free", "buff", "cache", "si",
"so", "bi", "bo", "in", "cs",
"us", "sy", "id", "wa" };
string strVMSTATEachLine = null;
string[] strVMSTATEachLineArray;
bool boolLineFlag = false;
ArrayList arrayListVMSTATLine = new ArrayList();
while ((strVMSTATEachLine = reader.ReadLine()) != null)
{
strVMSTATEachLineArray = strVMSTATEachLine.Split(' ');
//Check for the first line if it is valid
if (intNumberOfDataPoint == 0)
{
foreach (string strFirstLineVMSTAT in strFirstLineVMSTATArr)
{
boolLineFlag = false;
foreach (string strParseLine in strVMSTATEachLineArray)
{
if (strParseLine.CompareTo(strFirstLineVMSTAT) == 0)
{
boolLineFlag = true;
break;
}
}
if (boolLineFlag == false)
{
if (MessageBox.Show("\"" + strFirstLineVMSTAT +
"\" is not avalibale. The file may be invalid " +
"or corrupt.\nThere may be error in parsing the file. Do you " +
"want to continue ?", "VMSTAT Error",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question) == DialogResult.No)
{
Application.Exit();
throw new Exception("User has terminated the process.");
}
}
}
}
//Check for the second line if it is valid
if (intNumberOfDataPoint == 1)
{
foreach (string strSecondLineVMSTAT in strSecondLineVMSTATArr)
{
boolLineFlag = false;
foreach (string strParseLine in strVMSTATEachLineArray)
{
if (strParseLine.CompareTo(strSecondLineVMSTAT) == 0)
{
boolLineFlag = true;
break;
}
}
if (boolLineFlag == false)
{
if (MessageBox.Show("\"" + strSecondLineVMSTAT +
"\" is not avalibale. The file may be invalid or " +
"corrupt.\nThere may be error" +
" in parsing the file. Do you want " +
"to continue ?", "VMSTAT Error",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question) == DialogResult.No)
{
Application.Exit();
throw new Exception("User has terminated the process.");
}
}
}
}
//Check valididty for rest of the lines
if (intNumberOfDataPoint >= 2)
{
boolLineFlag = false;
arrayListVMSTATLine.Clear();
foreach (string strParseLine in strVMSTATEachLineArray)
{
if (strParseLine.CompareTo("") != 0)
{
int intParseLine = int.Parse(strParseLine);
arrayListVMSTATLine.Add(intParseLine);
}
}
object[] objStringArr = arrayListVMSTATLine.ToArray();
if (objStringArr.Length == 16)
{
boolLineFlag = true;
}
if (boolLineFlag == false)
{
if (MessageBox.Show("Line number : " + intNumberOfDataPoint +
" is not valid. The file may " +
"be invalid or corrupt or some value " +
"is missing in mentioned line " +
"number.\nThere may be error in parsing the file. " +
"Do you want to continue ?", "VMSTAT Error",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question) == DialogResult.No)
{
Application.Exit();
throw new Exception("User has terminated the process.");
}
}
}
intNumberOfDataPoint = intNumberOfDataPoint + 1;
}
reader.Close();
}
catch (Exception ee)
{
MessageBox.Show(ee.Message + "\n\nLine number : " +
intNumberOfDataPoint + " is not valid. " +
"The file may be invalid or corrupt " +
"or some value is missing in mentioned line number.",
"VMSTAT Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
这是用于通过绘制 X 和 Y 轴值以及描述和图表标题等其他信息来绘制图表的主要代码片段之一。
此方法确定要考虑哪个字段描述符,并根据用户需要显示该字段描述符的图表。这由全局变量 intGlobalGraphFieldDescriptorValue
确定。
//Create Graph and plot X and Y Axis along with other values
private void CreateGraph(ZedGraphControl zgc)
{
GraphPane myPane = zgc.GraphPane;
// Set the title and axis labels
myPane.Title.Text = strGlobalGraphTitle;
myPane.XAxis.Title.Text = strGlobalGraphXAxis;
myPane.YAxis.Title.Text = strGlobalGraphYAxis;
//Graph Field Descriptor is Procs
if (intGlobalGraphFieldDescriptorValue == 0)
{
ClearValues(zedGraphControl);
lblSubDesc1.Text = "r: The number of processes waiting for run time.";
lblSubDesc2.Text = "b: The number of processes in uninterruptible sleep.";
LineItem myCurve1 = myPane.AddCurve("r", null, CreatePointForY(0),
toolStripLine1.ForeColor, SymbolType.None);
myPane.XAxis.Scale.TextLabels = stringArrayGlobalDataPointForX;
LineItem myCurve2 = myPane.AddCurve("b", null,
CreatePointForY(1), toolStripLine2.ForeColor,
SymbolType.None);
myPane.XAxis.Scale.TextLabels = stringArrayGlobalDataPointForX;
}
//Graph Field Descriptor is Memory
if (intGlobalGraphFieldDescriptorValue == 1)
{
ClearValues(zedGraphControl);
lblSubDesc1.Text = "swpd: the amount of virtual memory used.";
lblSubDesc2.Text = "free: the amount of idle memory.";
lblSubDesc3.Text = "buff: the amount of memory used as buffers.";
lblSubDesc4.Text = "cache: the amount of memory used as cache.";
LineItem myCurve1 = myPane.AddCurve("swpd", null,
CreatePointForY(2), toolStripLine1.ForeColor, SymbolType.None);
myPane.XAxis.Scale.TextLabels = stringArrayGlobalDataPointForX;
LineItem myCurve2 = myPane.AddCurve("free", null,
CreatePointForY(3), toolStripLine2.ForeColor, SymbolType.None);
myPane.XAxis.Scale.TextLabels = stringArrayGlobalDataPointForX;
LineItem myCurve3 = myPane.AddCurve("buff", null,
CreatePointForY(4), toolStripLine3.ForeColor, SymbolType.None);
myPane.XAxis.Scale.TextLabels = stringArrayGlobalDataPointForX;
LineItem myCurve4 = myPane.AddCurve("cache", null,
CreatePointForY(5), toolStripLine4.ForeColor, SymbolType.None);
myPane.XAxis.Scale.TextLabels = stringArrayGlobalDataPointForX;
}
//Graph Field Descriptor is Swap
if (intGlobalGraphFieldDescriptorValue == 2)
{
ClearValues(zedGraphControl);
lblSubDesc1.Text = "si: Amount of memory swapped in from disk (/s).";
lblSubDesc2.Text = "so: Amount of memory swapped to disk (/s).";
LineItem myCurve1 = myPane.AddCurve("si", null,
CreatePointForY(6), toolStripLine1.ForeColor, SymbolType.None);
myPane.XAxis.Scale.TextLabels = stringArrayGlobalDataPointForX;
LineItem myCurve2 = myPane.AddCurve("so", null,
CreatePointForY(7), toolStripLine2.ForeColor, SymbolType.None);
myPane.XAxis.Scale.TextLabels = stringArrayGlobalDataPointForX;
}
//Graph Field Descriptor is IO
if (intGlobalGraphFieldDescriptorValue == 3)
{
ClearValues(zedGraphControl);
lblSubDesc1.Text = "bi: Blocks received from a block device (blocks/s).";
lblSubDesc2.Text = "bo: Blocks sent to a block device (blocks/s).";
LineItem myCurve1 = myPane.AddCurve("bi", null,
CreatePointForY(8), toolStripLine1.ForeColor, SymbolType.None);
myPane.XAxis.Scale.TextLabels = stringArrayGlobalDataPointForX;
LineItem myCurve2 = myPane.AddCurve("bo", null,
CreatePointForY(9), toolStripLine2.ForeColor, SymbolType.None);
myPane.XAxis.Scale.TextLabels = stringArrayGlobalDataPointForX;
}
//Graph Field Descriptor is System
if (intGlobalGraphFieldDescriptorValue == 4)
{
ClearValues(zedGraphControl);
lblSubDesc1.Text = "in: The number of interrupts per second, including the clock.";
lblSubDesc2.Text = "cs: The number of context switches per second.";
LineItem myCurve1 = myPane.AddCurve("in", null,
CreatePointForY(10), toolStripLine1.ForeColor, SymbolType.None);
myPane.XAxis.Scale.TextLabels = stringArrayGlobalDataPointForX;
LineItem myCurve2 = myPane.AddCurve("cs", null,
CreatePointForY(11), toolStripLine2.ForeColor, SymbolType.None);
myPane.XAxis.Scale.TextLabels = stringArrayGlobalDataPointForX;
}
//Graph Field Descriptor is CPU
if (intGlobalGraphFieldDescriptorValue == 5)
{
ClearValues(zedGraphControl);
lblSubDesc1.Text = "us: Time spent running non-kernel code. " +
"(user time, including nice time)";
lblSubDesc2.Text = "sy: Time spent running kernel code. (system time)";
lblSubDesc3.Text = "id: Time spent idle.";
lblSubDesc4.Text = "wa: Time spent waiting for IO.";
lblSubDesc5.Text = "cu: Total CPU Usage.";
LineItem myCurve1 = myPane.AddCurve("us", null,
CreatePointForY(12), toolStripLine1.ForeColor, SymbolType.None);
myPane.XAxis.Scale.TextLabels = stringArrayGlobalDataPointForX;
LineItem myCurve2 = myPane.AddCurve("sy", null,
CreatePointForY(13), toolStripLine2.ForeColor, SymbolType.None);
myPane.XAxis.Scale.TextLabels = stringArrayGlobalDataPointForX;
LineItem myCurve3 = myPane.AddCurve("id", null,
CreatePointForY(14), toolStripLine3.ForeColor, SymbolType.None);
myPane.XAxis.Scale.TextLabels = stringArrayGlobalDataPointForX;
LineItem myCurve4 = myPane.AddCurve("wa", null,
CreatePointForY(15), toolStripLine4.ForeColor, SymbolType.None);
myPane.XAxis.Scale.TextLabels = stringArrayGlobalDataPointForX;
LineItem myCurve5 = myPane.AddCurve("cu", null,
CreatePointForY(16), toolStripLine5.ForeColor, SymbolType.None);
myPane.XAxis.Scale.TextLabels = stringArrayGlobalDataPointForX;
}
// Calculate the Axis Scale Ranges
zgc.AxisChange();
RepaintScreen();
}
图表通过绘制 X 和 Y 轴以及统计值等其他信息显示后,将根据选定的字段描述符启用复选框。例如,如果用户选择 PROCS 作为字段描述符,则将为“r”(运行时进程)和“b”(不可中断睡眠进程)启用两个复选框。这意味着图表将显示两条用不同颜色分隔的线。
用户可以通过选择一个复选框并取消选择任何组件的另一个复选框进行过滤,从而一次分析一个组件。
//Recreate graph and plot X and Y Axis as per user selection
//by checking Checkbox of desired component
private void FilteredCreateGraph(ZedGraphControl zgc)
{
GraphPane myPane = zgc.GraphPane;
//Graph Field Descriptor is Procs
if (intGlobalGraphFieldDescriptorValue == 0)
{
if (checkBoxViewGraph1.Checked == true)
{
LineItem myCurve1 = myPane.AddCurve("r", null,
CreatePointForY(0), toolStripLine1.ForeColor, SymbolType.None);
}
if (checkBoxViewGraph2.Checked == true)
{
LineItem myCurve2 = myPane.AddCurve("b", null,
CreatePointForY(1), toolStripLine2.ForeColor, SymbolType.None);
}
}
//Graph Field Descriptor is Memory
if (intGlobalGraphFieldDescriptorValue == 1)
{
if (checkBoxViewGraph1.Checked == true)
{
LineItem myCurve1 = myPane.AddCurve("swpd", null,
CreatePointForY(2), toolStripLine1.ForeColor, SymbolType.None);
}
if (checkBoxViewGraph2.Checked == true)
{
LineItem myCurve2 = myPane.AddCurve("free", null,
CreatePointForY(3), toolStripLine2.ForeColor, SymbolType.None);
}
if (checkBoxViewGraph3.Checked == true)
{
LineItem myCurve3 = myPane.AddCurve("buff", null,
CreatePointForY(4), toolStripLine3.ForeColor, SymbolType.None);
}
if (checkBoxViewGraph4.Checked == true)
{
LineItem myCurve4 = myPane.AddCurve("cache", null,
CreatePointForY(5), toolStripLine4.ForeColor, SymbolType.None);
}
}
//Graph Field Descriptor is Swap
if (intGlobalGraphFieldDescriptorValue == 2)
{
if (checkBoxViewGraph1.Checked == true)
{
LineItem myCurve1 = myPane.AddCurve("si", null,
CreatePointForY(6), toolStripLine1.ForeColor, SymbolType.None);
}
if (checkBoxViewGraph2.Checked == true)
{
LineItem myCurve2 = myPane.AddCurve("so", null,
CreatePointForY(7), toolStripLine2.ForeColor, SymbolType.None);
}
}
//Graph Field Descriptor is IO
if (intGlobalGraphFieldDescriptorValue == 3)
{
if (checkBoxViewGraph1.Checked == true)
{
LineItem myCurve1 = myPane.AddCurve("bi", null,
CreatePointForY(8), toolStripLine1.ForeColor, SymbolType.None);
}
if (checkBoxViewGraph2.Checked == true)
{
LineItem myCurve2 = myPane.AddCurve("bo", null,
CreatePointForY(9), toolStripLine2.ForeColor, SymbolType.None);
}
}
//Graph Field Descriptor is System
if (intGlobalGraphFieldDescriptorValue == 4)
{
if (checkBoxViewGraph1.Checked == true)
{
LineItem myCurve1 = myPane.AddCurve("in", null,
CreatePointForY(10), toolStripLine1.ForeColor, SymbolType.None);
}
if (checkBoxViewGraph2.Checked == true)
{
LineItem myCurve2 = myPane.AddCurve("cs", null,
CreatePointForY(11), toolStripLine2.ForeColor, SymbolType.None);
}
}
//Graph Field Descriptor is CPU
if (intGlobalGraphFieldDescriptorValue == 5)
{
if (checkBoxViewGraph1.Checked == true)
{
LineItem myCurve1 = myPane.AddCurve("us", null,
CreatePointForY(12), toolStripLine1.ForeColor, SymbolType.None);
}
if (checkBoxViewGraph2.Checked == true)
{
LineItem myCurve2 = myPane.AddCurve("sy", null,
CreatePointForY(13), toolStripLine2.ForeColor, SymbolType.None);
}
if (checkBoxViewGraph3.Checked == true)
{
LineItem myCurve3 = myPane.AddCurve("id", null,
CreatePointForY(14), toolStripLine3.ForeColor, SymbolType.None);
}
if (checkBoxViewGraph4.Checked == true)
{
LineItem myCurve4 = myPane.AddCurve("wa", null,
CreatePointForY(15), toolStripLine4.ForeColor, SymbolType.None);
}
if (checkBoxViewGraph5.Checked == true)
{
LineItem myCurve5 = myPane.AddCurve("cu", null,
CreatePointForY(16), toolStripLine5.ForeColor, SymbolType.None);
}
}
zgc.AxisChange();
RepaintScreen();
}
代码中还有其他各种方法,您可能希望探索
UncheckAllToolStrip(object sender)
- 这将有助于取消选中字段描述符的所有ToolStripMenuItem
。SetSizeForZedGraphControl()
- 这有助于设置图表或图表控件的大小。ClearValues(ZedGraphControl zgc)
- 这有助于清除 Windows 表单中的所有值。ClearGraph(ZedGraphControl zgc)
- 它只清除图表内部的值。CreatePointForX()
- 这有助于以 HH:MM:SS 格式创建 X 轴的值。CreatePointForY(int intGraphField)
- 这有助于创建 Y 轴的值。DisplayMinAvgMaxPercStdDValue(int intMin, decimal decimalAvg, int intMax, double doublePercentileValue, double doublePopStdDeviation, int intGraphField)
- 这有助于根据用户选择的字段描述符在 Windows 表单中显示统计值。GetPercentileValue(double[] intArrayYValue)
- 这有助于计算单个组件的百分位数。GetPopulationStdDeviationValue(double[] intArrayYValue)
- 这有助于计算单个组件的标准差值。Export_To_CSV(string strFileName)
- 这有助于将统计值以 CSV 格式导出到用户所需的位置。
如果您想修改代码,请通过选择“项目”->“添加引用”来添加对 ZedGraph.dll(源代码中提供,或从 zedgraph.org 下载,它是基本的图表控件)的引用。
VMSTAT 日志文件示例
//VMSTAT Log file looks like below data
procs -----------memory---------- ---swap-- -----io---- --system-- ----cpu----
r b swpd free buff cache si so bi bo in cs us sy id wa
0 0 224 7016764 543576 22527872 0 0 4 16 0 0 4 1 96 0
0 0 224 7016700 543576 22527872 0 0 0 0 320 1317 0 0 99 0
0 0 224 7014812 543576 22527872 0 0 0 200 284 1171 1 0 98 0
0 0 224 7016508 543576 22527872 0 0 0 168 235 1173 0 0 100 0
...
...
如果您想下载 VMSTAT 日志文件示例(我在本例中使用的),请点击这里
关注点
用户将获得 VMSTAT 文件中每个组件的精美图表及其统计数据,s/he 可以将图表导出为各种图像格式,并将统计数据导出为 CSV 格式到用户选择的位置。
谢谢
我感谢 zedgraph.org 提供的出色图表类库。向 zedgraph 致敬,他们让我的生活变得轻松。
历史和未来增强功能
这是 VMSTAT_Analyzer 的第一个版本。我计划推出另一个版本,用于 VMSTAT 的 -a 选项,该选项提供活动内存分布。
如果您认为我可以在现有版本中添加任何其他功能来帮助 VMSTAT_Analyzer 成为更好的应用程序,请告诉我。