forked from heroku/erlang-in-anger
-
Notifications
You must be signed in to change notification settings - Fork 9
/
105-runtime-metrics.tex
506 lines (369 loc) · 34.9 KB
/
105-runtime-metrics.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
\chapter{Runtime Metrics}
\label{chap:runtime-metrics}
One of the best selling points of the Erlang VM for production use is how transparent it can be for all kinds of introspection, debugging, profiling, and analysis at run time.
The advantage of having these runtime metrics accessible programmatically is that building tools relying on them is easy, and building automation for some tasks or watchdogs is equally simple\footnote{Making sure your automated processes don't run away and go overboard with whatever corrective actions they take is more complex}. Then, in times of need, it's also possible to bypass the tools and go direct to the VM for information.
A practical approach to growing a system and keeping it healthy in production is to make sure all angles are observable: in the large, and in the small. There's no generic recipe to tell in advance what is going to be normal or not. You want to keep a lot of data and to look at it from time to time to form an idea about what your system looks like under normal circumstances. The day something goes awry, you will have all these angles you've grown to know, and it will be simpler to find what is off and needs fixing.
For this chapter (and most of those that follow), most of the concepts or features to be shown are accessible through code in the standard library, part of the regular OTP distribution.
However, these features aren't all in one place, and can make it too easy to shoot yourself in the foot within a production system. They also tend to be closer to building blocks than usable tools.
Therefore, to make the text lighter and to be more usable, common operations have been regrouped in the \otpapp{recon}\footnote{\href{http://ferd.github.io/recon/}{http://ferd.github.io/recon/}} library, and are generally production-safe.
\section{Global View}
\label{sec:global-view}
For a view of the VM in the large, it's useful to track statistics and metrics general to the VM, regardless of the code running on it. Moreover, you should aim for a solution that allows long-term views of each metric — some problems show up as a very long accumulation over weeks that couldn't be detected over small time windows.
Good examples for issues exposed by a long-term view include memory or process leaks, but also could be regular or irregular spikes in activities relative to the time of the day or week, which can often require having months of data to be sure about it.
For these cases, using existing Erlang metrics applications is useful. Common options are:
\begin{itemize*}
\item \otpapp{folsom}\footnote{\href{https://github.com/boundary/folsom}{https://github.com/boundary/folsom}} to store metrics in memory within the VM, whether global or app-specific..
\item \otpapp{vmstats}\footnote{\href{https://github.com/ferd/vmstats}{https://github.com/ferd/vmstats}} and \otpapp{statsderl}\footnote{\href{https://github.com/lpgauth/statsderl}{https://github.com/lpgauth/statsderl}}, sending node metrics over to graphite through \app{statsd}\footnote{\href{https://github.com/etsy/statsd/}{https://github.com/etsy/statsd/}}.
\item \otpapp{exometer}\footnote{\href{https://github.com/Feuerlabs/exometer}{https://github.com/Feuerlabs/exometer}}, a fancy-pants metrics system that can integrate with \otpapp{folsom} (among other things), and a variety of back-ends (graphite, collectd, \app{statsd}, Riak, SNMP, etc.). It's the newest player in town
\item \otpapp{ehmon}\footnote{\href{https://github.com/heroku/ehmon}{https://github.com/heroku/ehmon}} for output done directly to standard output, to be grabbed later through specific agents, splunk, and so on.
\item custom hand-rolled solutions, generally using ETS tables and processes periodically dumping the data.\footnote{Common patterns may fit the \otpapp{ectr} application, at \href{https://github.com/heroku/ectr}{https://github.com/heroku/ectr}}
\item or if you have nothing and are in trouble, a function printing stuff in a loop in a shell\footnote{The \otpapp{recon} application has the function \function{\href{http://ferd.github.io/recon/recon.html\#node\_stats\_print-2}{recon:node\_stats\_print/2}} to do this if you're in a pinch}.
\end{itemize*}
It is generally a good idea to explore them a bit, pick one, and get a persistence layer that will let you look through your metrics over time.
\subsection{Memory}
The memory reported by the Erlang VM in most tools will be a variant of what is reported by \expression{erlang:memory()}:
\begin{VerbatimEshell}
1> erlang:memory().
[{total,13772400},
{processes,4390232},
{processes_used,4390112},
{system,9382168},
{atom,194289},
{atom_used,173419},
{binary,979264},
{code,4026603},
{ets,305920}]
\end{VerbatimEshell}
This requires some explaining.
First of all, all the values returned are in bytes, and they represent memory \emph{allocated} (memory actively used by the Erlang VM, not the memory set aside by the operating system for the Erlang VM). It will sooner or later look much smaller than what the operating system reports.
The \expression{total} field contains the sum of the memory used for \expression{processes} and \expression{system} (which is incomplete, unless the VM is instrumented!). \expression{processes} is the memory used by Erlang processes, their stacks and heaps. \expression{system} is the rest: memory used by ETS tables, atoms in the VM, refc binaries\footnote{See Section \ref{sec:binaries}}, and some of the hidden data I mentioned was missing.
If you want the total amount of memory owned by the virtual machine, as in the amount that will trip system limits (\app{ulimit}), this value is more difficult to get from within the VM. If you want the data without calling \app{top} or \app{htop}, you have to dig down into the VM's memory allocators to find things out.\footnote{See Section \ref{subsec:erlang-memory-model}}
Fortunately, recon has the function \function{recon\_alloc:memory/1} to figure it out, where the argument is:
\begin{itemize*}
\item \expression{used} reports the memory that is actively used for allocated Erlang data;
\item \expression{allocated} reports the memory that is reserved by the VM. It includes the memory used, but also the memory yet-to-be-used but still given by the OS. This is the amount you want if you're dealing with \app{ulimit} and OS-reported values.
\item \expression{unused} reports the amount of memory reserved by the VM that is not being allocated. Equivalent to \expression{allocated - used}.
\item \expression{usage} returns a percentage (0.0 .. 1.0) of used over allocated memory ratios.
\end{itemize*}
There are additional options available, but you'll likely only need them when investigating memory leaks in chapter \ref{chap:memory-leaks}
\subsection{CPU}
\label{subsec:global-cpu}
Unfortunately for Erlang developers, CPU is very hard to profile. There are a few reasons for this:
\begin{itemize*}
\item The VM does a lot of work unrelated to processes when it comes to scheduling — high scheduling work and high amounts of work done by the Erlang processes are hard to characterize.
\item The VM internally uses a model based on \emph{reductions}, which represent an arbitrary number of work actions. Every function call, including BIFs, will increment a process reduction counter. After a given number of reductions, the process gets descheduled.
\item To avoid going to sleep when work is low, the threads that control the Erlang schedulers will do busy looping. This ensures the lowest latency possible for sudden load spikes. The VM flag \command{+sbwt none|very\_short|short|medium|long|very\_long} can be used to change this value.
\end{itemize*}
These factors combine to make it fairly hard to find a good absolute measure of how busy your CPU is actually running Erlang code. It will be common for Erlang nodes in production to do a moderate amount of work and use a lot of CPU, but to actually fit a lot of work in the remaining place when the workload gets higher.
The most accurate representation for this data is the scheduler wall time. It's an optional metric that needs to be turned on by hand on a node, and polled at regular intervals. It will reveal the time percentage a scheduler has been running processes and normal Erlang code, NIFs, BIFs, garbage collection, and so on, versus the amount of time it has spent idling or trying to schedule processes.
The value here represents \emph{scheduler utilization} rather than CPU utilization. The higher the ratio, the higher the workload.
While the basic usage is explained in the Erlang/OTP reference manual\footnote{\href{http://www.erlang.org/doc/man/erlang.html\#statistics\_scheduler\_wall\_time}{http://www.erlang.org/doc/man/erlang.html\#statistics\_scheduler\_wall\_time}}, the value can be obtained by calling recon:
\begin{VerbatimEshell}
1> recon:scheduler_usage(1000).
[{1,0.9919596133421669},
{2,0.9369579039389054},
{3,1.9294092120138725e-5},
{4,1.2087551402238991e-5}]
\end{VerbatimEshell}
The function \function{recon:scheduler\_usage(N)} will poll for \var{N} milliseconds (here, 1 second) and output the value of each scheduler. In this case, the VM has two very loaded schedulers (at 99.2\% and 93.7\% repectively), and two mostly unused ones at far below 1\%. Yet, a tool like \app{htop} would report something closer to this for each core:
\begin{VerbatimText}
[||||||||||||||||||||||||| 70.4%]
[||||||| 20.6%]
[|||||||||||||||||||||||||||||100.0%]
[|||||||||||||||| 40.2%]
\end{VerbatimText}
The result being that there is a decent chunk of CPU usage that would be mostly free for scheduling actual Erlang work (assuming the schedulers are busy waiting more than trying to select tasks to run), but is being reported as busy by the OS.
Another interesting behaviour possible is that the scheduler usage may show a higher rate (1.0) than what the OS will report. Schedulers waiting for os resources are considered utilized as they cannot handle more work. If the OS itself is holding up on non-CPU tasks it is still possible for Erlang's schedulers not to be able to do more work and report a full ratio.
These behaviours may especially be important to consider when doing capacity planning, and can be better indicators of headroom than looking at CPU usage or load.
%% 08:37 #erlounge: <+garazdawi> MononcQc: There is a cost when not doing work. The way it works is that when starting/stopping to do work it checks the time, but if the
%% scheduler has something to do the entire time then there is no overhead. So you might see a slight cpu increase if the schedulers run out of
%% work often, but it will (should) not affect the maximum possible throughput of the system.
\subsection{Processes}
\label{subsec:global-procs}
Trying to get a global view of processes is helpful when trying to assess how much work is being done in the VM in terms of \emph{tasks}. A general good practice in Erlang is to use processes for truly concurrent activities — on web servers, you will usually get one process per request or connection, and on stateful systems, you may add one process per-user — and therefore the number of processes on a node can be used as a metric for load.
Most tools mentioned in section \ref{sec:global-view} will track them in one way or another, but if the process count needs to be done manually, calling the following expression is enough:
\begin{VerbatimEshell}
1> length(processes()).
56535
\end{VerbatimEshell}
Tracking this value over time can be extremely helpful to try and characterize load or detect process leaks, along with other metrics you may have around.
\subsection{Ports}
\label{subsec:global-ports}
In a manner similar to processes, \emph{Ports} should be considered. Ports are a datatype that encompasses all kinds of connections and sockets opened to the outside world: TCP sockets, UDP sockets, SCTP sockets, file descriptors, and so on.
There is a general function (again, similar to processes) to count them: \expression{length(erlang:ports())}. However, this function merges in all types of ports into a single entity. Instead, one can use \otpapp{recon} to get them sorted by type:
\begin{VerbatimEshell}
1> recon:port_types().
[{"tcp_inet",21480},
{"efile",2},
{"udp_inet",2},
{"0/1",1},
{"2/2",1},
{"inet_gethost 4 ",1}]
\end{VerbatimEshell}
This list contains the types and the count for each type of port. The type name is a string and is defined by the Erlang VM itself.
All the \expression{*\_inet} ports are usually sockets, where the prefix is the protocol used (TCP, UDP, SCTP). The \expression{efile} type is for files, while \expression{"0/1"} and \expression{"2/2"} are file descriptors for standard I/O channels (\emph{stdin} and \emph{stdout}) and standard error channels (\emph{stderr}), respectively.
Most other types will be given names of the driver they're talking to, and will be examples of \emph{port programs}\footnote{\href{http://www.erlang.org/doc/tutorial/c\_port.html}{http://www.erlang.org/doc/tutorial/c\_port.html}} or \emph{port drivers}\footnote{\href{http://www.erlang.org/doc/tutorial/c\_portdriver.html}{http://www.erlang.org/doc/tutorial/c\_portdriver.html}}.
Again, tracking these can be useful to assess load or usage of a system, detect leaks, and so on.
\section{Digging In}
\label{sec:digging-in}
Whenever some 'in the large' view (or logging, maybe) has pointed you towards a potential cause for an issue you're having, it starts being interesting to dig around with a purpose. Is a process in a weird state? Maybe it needs tracing\footnote{See Chapter \ref{chap:tracing}}! Tracing is great whenever you have a specific function call or input or output to watch for, but often, before getting there, a lot more digging is required.
Outside of memory leaks, which often need their own specific techniques and are discussed in Chapter \ref{chap:memory-leaks}, the most common tasks are related to processes, and ports (file descriptors and sockets).
\subsection{Processes}
\label{subsec:digging-procs}
By all means, processes are an important part of a running Erlang system. And because they're so central to everything that goes on, there's a lot to want to know about them. Fortunately, the VM makes a lot of information available, some of which is safe to use, and some of which is unsafe to use in production (because they can return data sets large enough that the amount of memory copied to the shell process and used to print it can kill the node).
All the values can be obtained by calling \expression{process\_info(Pid, Key)} or \newline \expression{process\_info(Pid, [Keys])}\footnote{In cases where processes contain sensitive information, data can be forced to be kept private by calling \expression{process\_flag(sensitive, true)}}. Here are the commonly used keys\footnote{For \emph{all} options, look at \href{http://www.erlang.org/doc/man/erlang.html\#process\_info-2}{http://www.erlang.org/doc/man/erlang.html\#process\_info-2}}:
\begin{description*}
\item[Meta] \hfill
\begin{description}
\item[\expression{dictionary}] returns all the entries in the process dictionary\footnote{See \href{http://www.erlang.org/course/advanced.html\#dict}{http://www.erlang.org/course/advanced.html\#dict} and \href{http://ferd.ca/on-the-use-of-the-process-dictionary-in-erlang.html}{http://ferd.ca/on-the-use-of-the-process-dictionary-in-erlang.html}}. Generally safe to use, because people shouldn't be storing gigabytes of arbitrary data in there.
\item[\expression{group\_leader}] the group leader of a process defines where IO (files, output of \function{io:format/1-3}) goes.\footnote{See \href{http://learnyousomeerlang.com/building-otp-applications\#the-application-behaviour}{http://learnyousomeerlang.com/building-otp-applications\#the-application-behaviour} and \href{http://erlang.org/doc/apps/stdlib/io\_protocol.html}{http://erlang.org/doc/apps/stdlib/io\_protocol.html} for more details.}
\item[\expression{registered\_name}] if the process has a name (as registered with \function{erlang:register/2}), it is given here.
\item[\expression{status}] the nature of the process as seen by the scheduler. The possible values are:
\begin{description*}
\item[\expression{exiting}] the process is done, but not fully cleared yet;
\item[\expression{waiting}] the process is waiting in a \expression{receive ... end};
\item[\expression{running}] self-descriptive;
\item[\expression{runnable}] ready to run, but not scheduled yet because another process is running;
\item[\expression{garbage\_collecting}] self-descriptive;
\item[\expression{suspended}] whenever it is suspended by a BIF, or as a back-pressure mechanism because a socket or port buffer is full. The process only becomes runnable again once the port is no longer busy.
\end{description*}
\end{description}
\item[Signals] \hfill
\begin{description}
\item[\expression{links}] will show a list of all the links a process has towards other processes and also ports (sockets, file descriptors). Generally safe to call, but to be used with care on large supervisors that may return thousands and thousands of entries.
\item[\expression{monitored\_by}] gives a list of processes that are monitoring the current process (through the use of \function{erlang:monitor/2}).
\item[\expression{monitors}] kind of the opposite of \expression{monitored\_by}; it gives a list of all the processes being monitored by the one polled here.
\item[\expression{trap\_exit}] has the value \expression{true} if the process is trapping exits, \expression{false} otherwise.
\end{description}
\item[Location] \hfill
\begin{description}
\item[\expression{current\_function}] displays the current running function, as a tuple of the form \expression{\{Mod, Fun, Arity\}}.
\item[\expression{current\_location}] displays the current location within a module, as a tuple of the form \expression{\{Mod, Fun, Arity, [\{File, FileName\}, \{line, Num\}]\}}.
\item[\expression{current\_stacktrace}] more verbose form of the preceding option; displays the current stacktrace as a list of 'current locations'.
\item[\expression{initial\_call}] shows the function that the process was running when spawned, of the form \expression{\{Mod, Fun, Arity\}}. This may help identify what the process was spawned as, rather than what it's running right now.
\end{description}
\item[Memory Used] \hfill
\begin{description}
\item[\expression{binary}] Displays the all the references to refc binaries\footnote{See Section \ref{sec:binaries}} along with their size. Can be unsafe to use if a process has a lot of them allocated.
\item[\expression{garbage\_collection}] contains information regarding garbage collection in the process. The content is documented as 'subject to change' and should be treated as such. The information tends to contains entries such as the number of garbage collections the process has went through, options for full-sweep garbage collections, and heap sizes.
\item[\expression{heap\_size}] A typical Erlang process contains an 'old' heap and a 'new' heap, and goes through generational garbage collection. This entry shows the process' heap size for the newest generation, and it usually includes the stack size. The value returned is in \emph{words}.
\item[\expression{memory}] Returns, in \emph{bytes}, the size of the process, including the call stack, the heaps, and internal structures used by the VM that are part of a process.
\item[\expression{message\_queue\_len}] Tells you how many messages are waiting in the mailbox of a process.
\item[\expression{messages}] Returns all of the messages in a process' mailbox. This attribute is \emph{extremely} dangerous to request in production because mailboxes can hold millions of messages if you're debugging a process that managed to get locked up. \emph{Always} call for the \expression{message\_queue\_len} first to make sure it's safe to use.
\item[\expression{total\_heap\_size}] Similar to \expression{heap\_size}, but also contains all other fragments of the heap, including the old one. The value returned is in \emph{words}.
\end{description}
\item[Work] \hfill
\begin{description}
\item[\expression{reductions}] The Erlang VM does scheduling based on \emph{reductions}, an arbitrary unit of work that allows rather portable implementations of scheduling (time-based scheduling is usually hard to make work efficiently on as many OSes as Erlang runs on). The higher the reductions, the more work, in terms of CPU and function calls, a process is doing.
\end{description}
\end{description*}
Fortunately, for all the common ones that are also safe, recon contains the \expression{recon:info/1} function to help:
\begin{VerbatimEshell}
1> recon:info("<0.12.0>").
[{meta,[{registered_name,rex},
{dictionary,[{'$ancestors',[kernel_sup,<0.10.0>]},
{'$initial_call',{rpc,init,1}}]},
{group_leader,<0.9.0>},
{status,waiting}]},
{signals,[{links,[<0.11.0>]},
{monitors,[]},
{monitored_by,[]},
{trap_exit,true}]},
{location,[{initial_call,{proc_lib,init_p,5}},
{current_stacktrace,[{gen_server,loop,6,
[{file,"gen_server.erl"},{line,358}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,239}]}]}]},
{memory_used,[{memory,2808},
{message_queue_len,0},
{heap_size,233},
{total_heap_size,233},
{garbage_collection,[{min_bin_vheap_size,46422},
{min_heap_size,233},
{fullsweep_after,65535},
{minor_gcs,0}]}]},
{work,[{reductions,35}]}]
\end{VerbatimEshell}
For the sake of convenience, \expression{recon:info/1} will accept any pid-like first argument and handle it: literal pids, strings (\expression{"<0.12.0>"}), registered atoms, global names (\expression{\{global, Atom\}}), names registered with a third-party registry (e.g. with \otpapp{gproc}: \expression{\{via, gproc, Name\}}), or tuples (\expression{\{0,12,0\}}). The process just needs to be local to the node you're debugging.
If only a category of information is wanted, the category can be used directly:
\begin{VerbatimEshell}
2> recon:info(self(), work).
{work,[{reductions,11035}]}
\end{VerbatimEshell}
or can be used in exactly the same way as \function{process\_info/2}:
\begin{VerbatimEshell}
3> recon:info(self(), [memory, status]).
[{memory,10600},{status,running}]
\end{VerbatimEshell}
This latter form can be used to fetch unsafe information.
With all this data, it's possible to find out all we need to debug a system. The challenge then is often to figure out, between this per-process data, and the global one, which process(es) should be targeted.
When looking for high memory usage, for example it's interesting to be able to list all of a node's processes and find the top \var{N} consumers. Using the attributes above and the \function{recon:proc\_count(Attribute, N)} function, we can get these results:
\begin{VerbatimEshell}
4> recon:proc_count(memory, 3).
[{<0.26.0>,831448,
[{current_function,{group,server_loop,3}},
{initial_call,{group,server,3}}]},
{<0.25.0>,372440,
[user,
{current_function,{group,server_loop,3}},
{initial_call,{group,server,3}}]},
{<0.20.0>,372312,
[code_server,
{current_function,{code_server,loop,1}},
{initial_call,{erlang,apply,2}}]}]
\end{VerbatimEshell}
Any of the attributes mentioned earlier can work, and for nodes with long-lived processes that can cause problems, it's a fairly useful function.
There is however a problem when most processes are short-lived, usually too short to inspect through other tools, or when a moving window is what we need (for example, what processes are busy accumulating memory or running code \emph{right now}).
For this use case, Recon has the \function{recon:proc\_window(Attribute, Num, Milliseconds)} function.
It is important to see this function as a snapshot over a sliding window. A program's timeline during sampling might look like this:
\begin{Verbatim}
--w---- [Sample1] ---x-------------y----- [Sample2] ---z--->
\end{Verbatim}
The function will take two samples at an interval defined by \var{Milliseconds}.
Some processes will live between \var{w} and die at \var{x}, some between \var{y} and \var{z}, and some between \var{x} and \var{y}. These samples will not be too significant as they're incomplete.
If the majority of your processes run between a time interval \var{x} to \var{y} (in absolute terms), you should make sure that your sampling time is smaller than this so that for many processes, their lifetime spans the equivalent of \var{w} and \var{z}. Not doing this can skew the results: long-lived processes that have 10 times the time to accumulate data (say reductions) will look like huge consumers when they're not one.\footnote{Warning: this function depends on data gathered at two snapshots, and then building a dictionary with entries to differentiate them. This can take a heavy toll on memory when you have many tens of thousands of processes, and a little bit of time.}
The function, once running gives results like follows:
\begin{VerbatimEshell}
5> recon:proc_window(reductions, 3, 500).
[{<0.46.0>,51728,
[{current_function,{queue,in,2}},
{initial_call,{erlang,apply,2}}]},
{<0.49.0>,5728,
[{current_function,{dict,new,0}},
{initial_call,{erlang,apply,2}}]},
{<0.43.0>,650,
[{current_function,{timer,sleep,1}},
{initial_call,{erlang,apply,2}}]}]
\end{VerbatimEshell}
With these two functions, it becomes possible to hone in on a specific process that is causing issues or misbehaving.
\subsection{OTP Processes}
When processes in question are OTP processes (most of the processes in a production system should definitely be OTP processes), you instantly win more tools to inspect them.
In general the \module{sys} module\footnote{\href{http://www.erlang.org/doc/man/sys.html}{http://www.erlang.org/doc/man/sys.html}} is what you want to look into. Read the documentation on it and you'll discover why it's so useful. It contains the following features for any OTP process:
\begin{itemize*}
\item logging of all messages and state transitions, both to the shell or to a file, or even in an internal buffer to be queried;
\item statistics (reductions, message counts, time, and so on);
\item fetching the status of a process (metadata including the state);
\item fetching the state of a process (as in the \expression{\#state\{\}} record);
\item replacing that state
\item custom debugging functions to be used as callbacks
\end{itemize*}
It also provides functionality to suspend or resume process execution.
I won't go into a lot of details about these functions, but be aware that they exist.
\subsection{Ports}
Similarly to processes, Erlang ports allow a lot of introspection. The info can be accessed by calling \function{erlang:port\_info(Port, Key)}, and more info is available through the \module{inet} module. Most of it has been regrouped by the \function{recon:port\_info/1-2} functions, which work using a somewhat similar interface to their process-related counterparts.
\begin{description*}
\item[Meta] \hfill
\begin{description}
\item[\expression{id}] internal index of a port. Of no particular use except to differentiate ports.
\item[\expression{name}] type of the port — with names such as \expression{"tcp\_inet"}, \expression{"udp\_inet"}, or \expression{"efile"}, for example.
\item[\expression{os\_pid}] if the port is not an inet socket, but rather represents an external process or program, this value contains the os pid related to the said external program.
\end{description}
\item[Signals] \hfill
\begin{description}
\item[\expression{connected}] Each port has a controlling process in charge of it, and this process' pid is the \expression{connected} one.
\item[\expression{links}] ports can be linked with processes, much like other processes can be. The list of linked processes is contained here. Unless the process has been owned by or manually linked to a lot of processes, this should be safe to use.
\item[\expression{monitors}] ports that represent external programs can have these programs end up monitoring Erlang processes. These processes are listed here.
\end{description}
\item[IO] \hfill
\begin{description}
\item[\expression{input}] the number of bytes read from the port.
\item[\expression{output}] the number of bytes written to the port.
\end{description}
\item[Memory Used] \hfill
\begin{description}
\item[\expression{memory}] this is the memory (in bytes) allocated by the runtime system for the port. This number tends to be small-ish and excludes space allocated by the port itself.
\item[\expression{queue\_size}] Port programs have a specific queue, called the driver queue\footnote{The driver queue is available to queue output from the emulator to the driver (data from the driver to the emulator is queued by the emulator in normal Erlang message queues). This can be useful if the driver has to wait for slow devices etc, and wants to yield back to the emulator.}. This return the size of this queue, in bytes.
\end{description}
\item[Type-Specific] \hfill
\begin{description}
\item[Inet Ports] Returns inet-specific data, including statistics\footnote{\href{http://www.erlang.org/doc/man/inet.html\#getstat-1}{http://www.erlang.org/doc/man/inet.html\#getstat-1}}, the local address and port number for the socket (\expression{sockname}), and the inet options used\footnote{\href{http://www.erlang.org/doc/man/inet.html\#setopts-2}{http://www.erlang.org/doc/man/inet.html\#setopts-2}}
\item[Others] currently no other form than inet ports are supported in recon, and an empty list is returned.
\end{description}
\end{description*}
The list can be obtained as follows:
\begin{VerbatimEshell}
1> recon:port_info("#Port<0.818>").
[{meta,[{id,6544},{name,"tcp_inet"},{os_pid,undefined}]},
{signals,[{connected,<0.56.0>},
{links,[<0.56.0>]},
{monitors,[]}]},
{io,[{input,0},{output,0}]},
{memory_used,[{memory,40},{queue_size,0}]},
{type,[{statistics,[{recv_oct,0},
{recv_cnt,0},
{recv_max,0},
{recv_avg,0},
{recv_dvi,...},
{...}|...]},
{peername,{{50,19,218,110},80}},
{sockname,{{97,107,140,172},39337}},
{options,[{active,true},
{broadcast,false},
{buffer,1460},
{delay_send,...},
{...}|...]}]}]
\end{VerbatimEshell}
On top of this, functions to find out specific problematic ports exist the way they do for processes. The gotcha is that so far, recon only supports them for inet ports and with restricted attributes: the number of octets (bytes) sent, received, or both (\expression{send\_oct}, \expression{recv\_oct}, \expression{oct}, respectively), or the number of packets sent, received, or both (\expression{send\_cnt}, \expression{recv\_cnt}, \expression{cnt}, respectively).
So for the cumulative total, which can help find out who is slowly but surely eating up all your bandwidth:
\begin{VerbatimEshell}
2> recon:inet_count(oct, 3).
[{#Port<0.6821166>,15828716661,
[{recv_oct,15828716661},{send_oct,0}]},
{#Port<0.6757848>,15762095249,
[{recv_oct,15762095249},{send_oct,0}]},
{#Port<0.6718690>,15630954707,
[{recv_oct,15630954707},{send_oct,0}]}]
\end{VerbatimEshell}
Which suggest some ports are doing only input and eating lots of bytes. You can then use \function{recon:port\_info("\#Port<0.6821166>")} to dig in and find who owns that socket, and what is going on with it.
Or in any other case, we can look at what is sending the most data within any time window\footnote{See the explanations for the \function{recon:proc\_window/3} in the preceding subsection} with the \function{recon:inet\_window(Attribute, Count, Milliseconds)} function:
\begin{VerbatimEshell}
3> recon:inet_window(send_oct, 3, 5000).
[{#Port<0.11976746>,2986216,[{send_oct,4421857688}]},
{#Port<0.11704865>,1881957,[{send_oct,1476456967}]},
{#Port<0.12518151>,1214051,[{send_oct,600070031}]}]
\end{VerbatimEshell}
For this one, the value in the middle of the tuple is what \expression{send\_oct} was worth (or any chosen attribute for each call) during the specific time interval chosen (5 seconds here).
There is still some manual work involved into properly linking a misbehaving port to a process (and then possibly to a specific user or customer), but all the tools are in place.
%%%
\section{Exercises}
\subsection*{Review Questions}
\begin{enumerate}
\item What kind of values are reported for Erlang's memory?
\item What's a valuable process-related metric for a global view?
\item What's a port, and how should it be monitored globally?
\item Why can't you trust \app{top} or \app{htop} for CPU usage with Erlang systems? What's the alternative?
\item Name two types of signal-related information available for processes
\item How can you find what code a specific process is running?
\item What are the different kinds of memory information available for a specific process?
\item How can you know if a process is doing a lot of work?
\item Name a few of the values that are dangerous to fetch when inspecting processes in a production system.
\item What are some features provided to OTP processes through the sys module?
\item What kind of values are available when inspecting inet ports?
\item How can you find the type of a port (Files, TCP, UDP)?
\end{enumerate}
\subsection*{Open-ended Questions}
\begin{enumerate}
\item Why do you want a long time window available on global metrics?
\item Which would be more appropriate between \function{recon:proc\_count/2} and \function{recon:proc\_window/3} to find issues with:
\begin{enumerate*}
\item Reductions \item Memory \item Message queue length
\end{enumerate*}
\item How can you find information about who is the supervisor of a given process?
\item When should you use \function{recon:inet\_count/2}? \function{recon:inet\_window/3}?
\item What could explain the difference in memory reported by the operating system and the memory functions in Erlang?
\item Why is it that Erlang can sometimes look very busy even when it isn't?
\item How can you find what proportion of processes on a node are ready to run, but can't be scheduled right away?
\end{enumerate}
\subsection*{Hands-On}
Using the code at \href{https://github.com/ferd/recon\_demo}{https://github.com/ferd/recon\_demo}:
\begin{enumerate}
\item What's the system memory?
\item Is the node using a lot of CPU resources?
\item Is any process mailbox overflowing?
\item Which chatty process (\module{council\_member}) takes the most memory?
\item Which chatty process is eating the most CPU?
\item Which chatty process is consuming the most bandwidth?
\item Which chatty process sends the most messages over TCP? The least?
\item Can you find out if a specific process tends to hold multiple connections or file descriptors open at the same time on a node?
\item Can you find out which function is being called by the most processes at once on the node right now?
\end{enumerate}