├── Binary
└── SQLServerDashboard.zip
├── Docs
├── BlockOnDB.png
├── Dashboard-600.png
├── Dashboard-800.png
├── Dashboard.png
├── DatabaseFiles.png
├── ExpensiveStoredProc.png
├── Graphs.png
├── MostExpensiveQueries.png
├── PerformanceCounters.png
├── QueryDetailView.png
├── Sessions.png
├── WhatsGoingOn.png
└── web.config
├── README.md
└── Source
├── SQLServerDashboard.sln
├── SQLServerDashboard.v12.suo
├── SQLServerDashboard
├── Blocks.aspx
├── CPU.ashx
├── CPU.ashx.cs
├── CPUTab.aspx
├── CurrentSessions.aspx
├── CurrentSessions.aspx.cs
├── CurrentSessions.aspx.designer.cs
├── Dashboard.aspx
├── DatabaseFiles.aspx
├── Default.aspx
├── ExpensiveQueries.aspx
├── ExpensiveStoredProc.aspx
├── HistoryTab.aspx
├── InactiveSessions.aspx
├── PerformanceCounters.aspx
├── Processes.aspx
├── Properties
│ └── AssemblyInfo.cs
├── SQLServerDashboard.csproj
├── SQLServerDashboard.csproj.user
├── Scripts
│ ├── jquery-1.7.1.intellisense.js
│ ├── jquery-1.7.1.js
│ └── jquery-1.7.1.min.js
├── Sessions.aspx
├── SessionsTab.aspx
├── Summary.aspx
├── TestFlot.html
├── Todo.txt
├── WaitsTab.aspx
├── Web.Debug.config
├── Web.Release.config
├── Web.config
├── WhoIsActive.aspx
├── bin
│ ├── SQLServerDashboard.dll
│ └── SQLServerDashboard.pdb
├── cpu.aspx
├── css
│ ├── basic.css
│ ├── basic_ie.css
│ ├── bootstrap-theme.css
│ ├── bootstrap-theme.css.map
│ ├── bootstrap-theme.min.css
│ ├── bootstrap.css
│ ├── bootstrap.css.map
│ ├── bootstrap.min.css
│ └── dashboard.css
├── fonts
│ ├── glyphicons-halflings-regular.eot
│ ├── glyphicons-halflings-regular.svg
│ ├── glyphicons-halflings-regular.ttf
│ └── glyphicons-halflings-regular.woff
├── idle.html
├── img
│ └── basic
│ │ └── x.png
└── js
│ ├── Dashboard.js
│ ├── ScriptsForWidgets.js
│ ├── bootstrap.js
│ ├── bootstrap.min.js
│ ├── jquery-1.11.1.min.js
│ ├── jquery.fixedheadertable.min.js
│ ├── jquery.flot.baseline.js
│ ├── jquery.flot.js
│ ├── jquery.flot.time.min.js
│ ├── jquery.idletimer.js
│ └── jquery.simplemodal.js
├── deploy.ps1
└── gitpush.ps1
/Binary/SQLServerDashboard.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oazabir/SQLServerPerformanceDashboard/3847e8003f266d5ebb0926d585d5cb7f8adce0a6/Binary/SQLServerDashboard.zip
--------------------------------------------------------------------------------
/Docs/BlockOnDB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oazabir/SQLServerPerformanceDashboard/3847e8003f266d5ebb0926d585d5cb7f8adce0a6/Docs/BlockOnDB.png
--------------------------------------------------------------------------------
/Docs/Dashboard-600.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oazabir/SQLServerPerformanceDashboard/3847e8003f266d5ebb0926d585d5cb7f8adce0a6/Docs/Dashboard-600.png
--------------------------------------------------------------------------------
/Docs/Dashboard-800.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oazabir/SQLServerPerformanceDashboard/3847e8003f266d5ebb0926d585d5cb7f8adce0a6/Docs/Dashboard-800.png
--------------------------------------------------------------------------------
/Docs/Dashboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oazabir/SQLServerPerformanceDashboard/3847e8003f266d5ebb0926d585d5cb7f8adce0a6/Docs/Dashboard.png
--------------------------------------------------------------------------------
/Docs/DatabaseFiles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oazabir/SQLServerPerformanceDashboard/3847e8003f266d5ebb0926d585d5cb7f8adce0a6/Docs/DatabaseFiles.png
--------------------------------------------------------------------------------
/Docs/ExpensiveStoredProc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oazabir/SQLServerPerformanceDashboard/3847e8003f266d5ebb0926d585d5cb7f8adce0a6/Docs/ExpensiveStoredProc.png
--------------------------------------------------------------------------------
/Docs/Graphs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oazabir/SQLServerPerformanceDashboard/3847e8003f266d5ebb0926d585d5cb7f8adce0a6/Docs/Graphs.png
--------------------------------------------------------------------------------
/Docs/MostExpensiveQueries.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oazabir/SQLServerPerformanceDashboard/3847e8003f266d5ebb0926d585d5cb7f8adce0a6/Docs/MostExpensiveQueries.png
--------------------------------------------------------------------------------
/Docs/PerformanceCounters.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oazabir/SQLServerPerformanceDashboard/3847e8003f266d5ebb0926d585d5cb7f8adce0a6/Docs/PerformanceCounters.png
--------------------------------------------------------------------------------
/Docs/QueryDetailView.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oazabir/SQLServerPerformanceDashboard/3847e8003f266d5ebb0926d585d5cb7f8adce0a6/Docs/QueryDetailView.png
--------------------------------------------------------------------------------
/Docs/Sessions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oazabir/SQLServerPerformanceDashboard/3847e8003f266d5ebb0926d585d5cb7f8adce0a6/Docs/Sessions.png
--------------------------------------------------------------------------------
/Docs/WhatsGoingOn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oazabir/SQLServerPerformanceDashboard/3847e8003f266d5ebb0926d585d5cb7f8adce0a6/Docs/WhatsGoingOn.png
--------------------------------------------------------------------------------
/Docs/web.config:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oazabir/SQLServerPerformanceDashboard/3847e8003f266d5ebb0926d585d5cb7f8adce0a6/Docs/web.config
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | SQL Server Performance Dashboard (SSPD)
2 | =======================================
3 |
4 | ## Introduction
5 | SQL Server Performance Dashboard (SSPD) is a small website that shows you performance & problems of one or more SQL Server instances and their databases in near real time. It uses the Dynamic Management Views (DMV) to gather useful data from the verbose output and combines them with utility stored procs in order to get meaningful, easy to understand information out of them. You can use it to quickly spot blocking queries, who is blocking who, expensive query that are consuming high CPU or disk, see if there's unusual locks, very high disk activity and so on.
6 |
7 | ![enter image description here][1]
8 |
9 | ## Get the code
10 | The binaries are here, which you can just extract into a IIS folder, put the connection strings in the web.config file for a user that has elevated privilege, and you are done.
11 | [SqlServerDashboard GitHub Project Binaries][2]
12 |
13 | If you aren't using the sa user, but your own user, give the user the right permission:
14 |
15 | USE master;
16 | GO
17 | GRANT VIEW SERVER STATE TO MyUser;
18 | GO
19 |
20 | Here's a sample connection string:
21 |
22 |
23 |
24 | Or you can get the source code from the GitHub project site:
25 | [https://github.com/oazabir/SQLServerDashboard][3]
26 |
27 | ## Why not use SQL Server Management Studio?
28 | SQL Server Management Studio comes with Activity Report that shows real time performance of the server, as well as various reports to show you top queries, blocked sessions etc. Those are quite useful surely, but what we need is someone telling us exactly what is wrong with the query or what is wrong with a particular WAIT or what is really wrong with some graph that looks high. For ex, on the Top IO query report, it would be useful to know what part of the query is causing high IO. Is there a table scan? Index scan? Similarly, on the blocked session report, it would be useful to know who is blocking who. SSPD tries to make it painless for us by telling us exactly what is wrong. Moreover, it is available as a website, readily available without having to launch SQL Server Management Studio and running the reports. Also SSPD refreshes real time, showing you the latest state of the server every 5-10 seconds.
29 |
30 | ## What's happening on the server now?
31 | SSPD uses some custom made scripts to query the DMVs and then make sense out of it. For ex, let's look at the first one: "What's going on." It shows you near real-time view of what queries are running at this moment, and it will also tell you what is really bad about the queries:
32 |
33 | ![What's going on][4]
34 |
35 | It uses the famous [Who is Active][5] script made by Adam Machanic, that gives a very informative view of what is currently happening on the server.
36 |
37 | Once it pulls the output, it binds to a `DataGrid` and for each row, and then it checks if there's something off the chart:
38 |
39 |
40 |
41 |
42 |
43 | <%# Convert.ToDecimal(Eval("CPU")) > 1000 ? "High CPU" : "" %>
44 | <%# Convert.ToDecimal(Eval("physical_reads")) > 1000 ? "High Physical Read" : "" %>
45 |
46 |
47 |
48 |
49 | No Query running at the moment.
50 |
51 |
52 |
53 | Here you see, it looks at specific values and if the values are unusually high, it injects a warning label. This saves you from scanning through the output to see if something is off. You can easily set the threshold values you want and get instant warning labels on the screen.
54 |
55 | Wherever you see a query, you can click on it to view the full details.
56 |
57 | ![Full query view][6]
58 |
59 | ## Most expensive queries
60 | This is probably the most useful one for day to day monitoring. It queries the cached plan for the queries SQL Server has run since it was last restarted or `DBCC FREEPROCCACHE` was called. Then it shows you not just the most expensive query, but also exactly what part of the query is causing the problem. You can see the WHERE clause that is causing the highest IO load.
61 |
62 | ![Most expensive queries][7]
63 |
64 | Here you can see some table scan going on. You can also see the part of the WHERE clause that is causing the table scan.
65 | If you click on the query (scrolling right), it will show the full query.
66 |
67 | ## Expensive stored proc
68 | Next one is the Expensive Stored Proc view, which shows you the most expensive stored procs in terms of CPU or IO.
69 |
70 | ![Expensive Stored Proc][8]
71 |
72 | You can see here the `AvgLogicalReads` for `QueryOrders` is very high. That stored proc is killing the database.
73 |
74 | ## How it works
75 | ### Look ma, no AJAX!
76 | You will notice that the panels are refreshing periodically. You might think I am using AJAX to call some serverside web service in order to get JSON/XML response, and then use some jQuery template to render the html output. Nope. I am using what our ancestors have happily used for generations. The natural, organic IFRAME solution, with no side effect. The html output for each panel comes from individual ASP.NET pages, via IFRAMEs and then they get injected into a DIV on the main Dashboard page.
77 |
78 | There are several benefits to this approach:
79 |
80 | - The widgets are individual pages, which user can browse directly in full browser view.
81 | - Each widget is a plain ASP.NET page. No need to build webservices to return data in JSON/XML format. No need for any entity classes either that you usually use to serialize into JSON/XML.
82 | - The HTML content is generated server side, using regular ASP.NET. Thus there's no need to use any Javascript based HTML templating library.
83 | - As there's no need for AJAX or html templating, there's no need to worry about jQuery or its plugin breaking compatibility in new versions, and updating javascript libraries regularly.
84 |
85 | Let's see how to do this. First the HTML markup to draw the panels:
86 |
87 |
98 |
99 | This is the markup taken from the [Twitter Bootstrap theme][9].
100 |
101 | You will notice there's an invisible IFRAME there. When the IFRAME loads, it calls the `setContent` function. That function takes the whole content of the IFRAME and injects inside the panel-body div.
102 |
103 | function setContent(iframe, id) {
104 | ...
105 | $('#' + id)
106 | .html($(iframe).contents().find("form").html())
107 | .dblclick(function () {
108 | iframe.contentWindow.location.reload();
109 | })
110 | ...
111 | }
112 |
113 | There you go, clean AJAX-like solution without any AJAX: no XMLHTTP, no JSON plumbing, no HTML templating, no server-side webservice.
114 |
115 | Now this would not work for any event handler that is hooked inside the IFRAME. So, how does the click on a query show the popup window with the full query? Also if it was an IFRAME, shouldn't the popup actually come inside the IFRAME?
116 |
117 | The click functionality is done on the main Dashboard page. After injecting the content into the DIV, it hooks the click handlers that shows the popup on the main page:
118 |
119 | function setContent(iframe, id) {
120 | $('#' + id)
121 | .find('td.large-cell').off('click');
122 |
123 | if ($('#' + id).scrollLeft() == 0) {
124 | $('#' + id)
125 | .html($(iframe).contents().find("form").html())
126 | .dblclick(function () {
127 | iframe.contentWindow.location.reload();
128 | })
129 | .find('td.large-cell').find('div').click(function () {
130 | $('#content_text').text($(this).html());
131 | $('#basic-modal-content').modal();
132 | });
133 | }
134 |
135 | Here's it looks for any
having the class large-cell. It then hooks the click even on it and shows the modal dialog box. The modal dialog box is from [Eric Martin's SimpleModal][10] plugin.
136 |
137 | ### Plotting the charts
138 | The chart uses the jQuery plugin [Flot][11] to render some of the performance counters as running charts.
139 |
140 | ![Flot charts][12]
141 |
142 | There's a PerformanceCounter.aspx which is responsible for rendering the table showing the performance counters. It picks some important counters, and marks them to appear on the chart. First it runs through the table, looking for the counters, and marks the label of the counter as x-axis and value of the counter as y-axis:
143 |
144 | var plot = ["Batch Requests/sec", "Full Scans/sec", "SQL Compilations/sec", "Total Latch Wait Time (ms)"];
145 | $('td').each(function (i, e) {
146 | td = $(e);
147 | if (td.text().trim().length > 0) {
148 | for (var i = 0; i < plot.length; i ++) {
149 | if (plot[i] == td.text().trim()) {
150 | if (td.prev().text() == "_Total" || td.prev().text().trim().length == 0) {
151 | td.addClass("x-axis");
152 | td.next().addClass("y-axis");
153 | }
154 | }
155 | }
156 | }
157 | })
158 |
159 | Now this page is hosted on the Dashboard page inside an IFRAME. So, the Dashboard page scans the IFRAME content, looks for these labels, picks their values and passes to the Flot chart plugin:
160 |
161 | $(iframe).contents().find("form").find(".x-axis").each(function (i, e) {
162 | var x = $(e);
163 | var y = x.next('.y-axis');
164 | var xname = x.text();
165 | var yvalue = parseInt(y.text());
166 | if (datasets[xname]) {
167 | var data = datasets[xname].data;
168 |
169 | data.pop();
170 |
171 | data.splice(0, 0, yvalue);
172 | }
173 | });
174 |
175 | Rest of the job of updating the Flot chart is done by the usual Flot code:
176 |
177 | function updatePlot() {
178 | var index = 0;
179 |
180 | $.each(datasets, function (key, val) {
181 | var items = [];
182 | for (var i = 0; i < val.data.length; i++)
183 | items.push([i, val.data[i]]);
184 |
185 | var data = { color: val.color, data: items };
186 |
187 | if (plots[index] != null) {
188 | plot = plots[index];
189 | plot.setData([data]);
190 | plot.draw();
191 | }
192 | else {
193 | plot = $.plot("#placeholder" + (index + 1), [data], {
194 | series: {
195 | //shadowSize: 0 // Drawing is faster without shadows
196 | },
197 | lines: { show: true, fill: true },
198 | grid: {
199 | hoverable: true,
200 | clickable: true
201 | },
202 | yaxis: {
203 | min: 0,
204 | max: val.ymax
205 | },
206 | xaxis: {
207 | show: false
208 | }
209 | });
210 |
211 | That's it! Again, no AJAX, no webservice, no html templating, no JSON plubing. Pure organic IFRAME and html.
212 |
213 | ## Conclusion
214 | SSPD tries to make it easy for us to monitor SQL Server health. It gives meainingful information out of the verbose data produced by SQL Server's built-in views. It shows you exactly where the problem is. You can customize the code anyway you like, adding your own warnings, own thresholds, and make it suit your specific need.
215 |
216 | [1]: https://raw.githubusercontent.com/oazabir/SQLServerPerformanceDashboard/master/Docs/Dashboard-800.png
217 | [2]: https://github.com/oazabir/SQLServerPerformanceDashboard/blob/master/Binary/SQLServerDashboard.zip?raw=true
218 | [3]: https://github.com/oazabir/SQLServerPerformanceDashboard
219 | [4]: https://raw.githubusercontent.com/oazabir/SQLServerPerformanceDashboard/master/Docs/WhatsGoingOn.png
220 | [5]: http://sqlblog.com/blogs/adam_machanic/archive/2012/03/22/released-who-is-active-v11-11.aspx
221 | [6]: https://raw.githubusercontent.com/oazabir/SQLServerPerformanceDashboard/master/Docs/QueryDetailView.png
222 | [7]: https://raw.githubusercontent.com/oazabir/SQLServerPerformanceDashboard/master/Docs/MostExpensiveQueries.png
223 | [8]: https://raw.githubusercontent.com/oazabir/SQLServerPerformanceDashboard/master/Docs/ExpensiveStoredProc.png
224 | [9]: http://getbootstrap.com/examples/theme/
225 | [10]: http://www.ericmmartin.com/projects/simplemodal/
226 | [11]: http://www.flotcharts.org/
227 | [12]: https://raw.githubusercontent.com/oazabir/SQLServerPerformanceDashboard/master/Docs/Graphs.png
228 |
--------------------------------------------------------------------------------
/Source/SQLServerDashboard.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.21005.1
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SQLServerDashboard", "SQLServerDashboard\SQLServerDashboard.csproj", "{7E284626-B72E-4C57-8063-7F8B3B35B4BD}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {7E284626-B72E-4C57-8063-7F8B3B35B4BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {7E284626-B72E-4C57-8063-7F8B3B35B4BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {7E284626-B72E-4C57-8063-7F8B3B35B4BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {7E284626-B72E-4C57-8063-7F8B3B35B4BD}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------
/Source/SQLServerDashboard.v12.suo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oazabir/SQLServerPerformanceDashboard/3847e8003f266d5ebb0926d585d5cb7f8adce0a6/Source/SQLServerDashboard.v12.suo
--------------------------------------------------------------------------------
/Source/SQLServerDashboard/Blocks.aspx:
--------------------------------------------------------------------------------
1 | <%@ Page Language="C#" EnableViewState="false"%>
2 | <%@ OutputCache NoStore="true" Location="None" %>
3 |
4 |
5 |
6 |
7 |
8 | Sessions
9 |
10 |
11 |
12 |
13 |
14 |
52 |
53 |
73 |
79 |
80 |
--------------------------------------------------------------------------------
/Source/SQLServerDashboard/CPU.ashx:
--------------------------------------------------------------------------------
1 | <%@ WebHandler Language="C#" CodeBehind="CPU.ashx.cs" Class="SQLServerDashboard.CPU" %>
2 |
--------------------------------------------------------------------------------
/Source/SQLServerDashboard/CPU.ashx.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Configuration;
4 | using System.Data.SqlClient;
5 | using System.Text;
6 | using System.Web;
7 |
8 | namespace SQLServerDashboard
9 | {
10 | ///
11 | /// Summary description for CPU
12 | ///
13 | public class CPU : IHttpHandler
14 | {
15 |
16 | public void ProcessRequest(HttpContext context)
17 | {
18 | string connectionName = context.Request["c"];
19 | using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[connectionName].ConnectionString))
20 | {
21 | conn.Open();
22 | using (SqlCommand cmd = new SqlCommand())
23 | {
24 | cmd.Connection = conn;
25 | cmd.CommandText = @"
26 | declare @ts_now bigint
27 | select @ts_now = ms_ticks from sys.dm_os_sys_info
28 |
29 | select top 30 record_id,
30 | dateadd (ms, (y.[timestamp] -@ts_now), GETDATE()) as EventTime,
31 | SQLProcessUtilization as SQLServer,
32 | --SystemIdle,
33 | 100 - SystemIdle - SQLProcessUtilization as Others
34 | from (
35 | select
36 | record.value('(./Record/@id)[1]', 'int') as record_id,
37 | record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int')
38 | as SystemIdle,
39 | record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]',
40 | 'int') as SQLProcessUtilization,
41 | timestamp
42 | from (
43 | select timestamp, convert(xml, record) as record
44 | from sys.dm_os_ring_buffers
45 | where ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR'
46 | and record like '%%') as x
47 | ) as y
48 | order by record_id desc
49 |
50 | ";
51 | cmd.CommandType = System.Data.CommandType.Text;
52 |
53 | StringBuilder buf = new StringBuilder();
54 | //using (OracleDataReader dr = cmd.ExecuteReader())
55 | // WriteDataReader(buf, dr);
56 | using (SqlDataReader dr1 = cmd.ExecuteReader())
57 | {
58 | buf.Append("([");
59 | while (dr1.Read())
60 | {
61 | double sqlServer = Convert.ToDouble(dr1["SQLServer"]);
62 | double others = Convert.ToDouble(dr1["Others"]);
63 | double total = sqlServer + others;
64 | buf.Append(string.Format(
65 | "{{'Total time':{0}, 'SQLServer': {1}, 'Others': {2}, 'EventTime': {3}}},",
66 | Convert.ToString(total),
67 | Convert.ToString(sqlServer),
68 | Convert.ToString(others),
69 | Convert.ToDateTime(dr1["EventTime"]).Ticks/10000));
70 | }
71 | if (buf[buf.Length - 1] == ',')
72 | buf.Remove(buf.Length - 1, 1);
73 | buf.Append("])");
74 | }
75 |
76 | context.Response.ContentType = "application/json";
77 | context.Response.Write(buf.ToString());
78 | }
79 | }
80 |
81 | }
82 |
83 | public bool IsReusable
84 | {
85 | get
86 | {
87 | return false;
88 | }
89 | }
90 | }
91 | }
--------------------------------------------------------------------------------
/Source/SQLServerDashboard/CPUTab.aspx:
--------------------------------------------------------------------------------
1 | <%@ Page Language="C#" %>
2 |
3 | <%@ OutputCache NoStore="true" Location="None" %>
4 |
5 |
6 |
7 |
8 |
9 |
10 | CPU Monitor
11 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
29 |
57 |
58 |
59 |
144 |
145 |