aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2014-11-04 06:45:59 -0500
committerTakashi Iwai <tiwai@suse.de>2014-11-04 08:09:14 -0500
commitf5914908a5b7b2338f210e56827a1ef35585dc6d (patch)
tree27349a1680e4d09f0ee3f604579c8146c3ad97b0
parentd507941beb1ef98c19e2902007aee4faf36f854f (diff)
ALSA: pcm: Replace PCM hwptr tracking with tracepoints
ALSA PCM core has a mechanism tracking the PCM hwptr updates for analyzing XRUNs. But its log is limited (up to 10) and its log output is a kernel message, which is hard to handle. In this patch, the hwptr logging is moved to the tracing infrastructure instead of its own. Not only the hwptr updates but also XRUN and hwptr errors are recorded on the trace log, so that user can see such events at the exact timing. The new "snd_pcm" entry will appear in the tracing events: # ls -F /sys/kernel/debug/tracing/events/snd_pcm enable filter hw_ptr_error/ hwptr/ xrun/ The hwptr is for the regular hwptr update events. An event trace looks like: aplay-26187 [004] d..3 4012.834761: hwptr: pcmC0D0p/sub0: POS: pos=488, old=0, base=0, period=1024, buf=16384 "POS" shows the hwptr update by the explicit position update call and "IRQ" means the hwptr update by the interrupt, i.e. snd_pcm_period_elapsed() call. The "pos" is the passed ring-buffer offset by the caller, "old" is the previous hwptr, "base" is the hwptr base position, "period" and "buf" are period- and buffer-size of the target PCM substream. (Note that the hwptr position displayed here isn't the ring-buffer offset. It increments up to the PCM position boundary.) The XRUN event appears similarly, but without "pos" field. The hwptr error events appear with the PCM identifier and its reason string, such as "Lost interrupt?". The XRUN and hwptr error reports on kernel message are still left, can be turned on/off via xrun_debug proc like before. But the bit 3, 4, 5 and 6 bits of xrun_debug proc are dropped by this patch. Also, along with the change, the message strings have been reformatted to be a bit more consistent. Last but not least, the hwptr reporting is enabled only when CONFIG_SND_PCM_XRUN_DEBUG is set. Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--Documentation/sound/alsa/Procfile.txt13
-rw-r--r--sound/core/Makefile3
-rw-r--r--sound/core/pcm_lib.c146
-rw-r--r--sound/core/pcm_trace.h110
4 files changed, 137 insertions, 135 deletions
diff --git a/Documentation/sound/alsa/Procfile.txt b/Documentation/sound/alsa/Procfile.txt
index 7fcd1ad96fcc..cfc49567b9dc 100644
--- a/Documentation/sound/alsa/Procfile.txt
+++ b/Documentation/sound/alsa/Procfile.txt
@@ -101,10 +101,6 @@ card*/pcm*/xrun_debug
101 bit 0 = Enable XRUN/jiffies debug messages 101 bit 0 = Enable XRUN/jiffies debug messages
102 bit 1 = Show stack trace at XRUN / jiffies check 102 bit 1 = Show stack trace at XRUN / jiffies check
103 bit 2 = Enable additional jiffies check 103 bit 2 = Enable additional jiffies check
104 bit 3 = Log hwptr update at each period interrupt
105 bit 4 = Log hwptr update at each snd_pcm_update_hw_ptr()
106 bit 5 = Show last 10 positions on error
107 bit 6 = Do above only once
108 104
109 When the bit 0 is set, the driver will show the messages to 105 When the bit 0 is set, the driver will show the messages to
110 kernel log when an xrun is detected. The debug message is 106 kernel log when an xrun is detected. The debug message is
@@ -121,15 +117,6 @@ card*/pcm*/xrun_debug
121 buggy) hardware that doesn't give smooth pointer updates. 117 buggy) hardware that doesn't give smooth pointer updates.
122 This feature is enabled via the bit 2. 118 This feature is enabled via the bit 2.
123 119
124 Bits 3 and 4 are for logging the hwptr records. Note that
125 these will give flood of kernel messages.
126
127 When bit 5 is set, the driver logs the last 10 xrun errors and
128 the proc file shows each jiffies, position, period_size,
129 buffer_size, old_hw_ptr, and hw_ptr_base values.
130
131 When bit 6 is set, the full xrun log is shown only once.
132
133card*/pcm*/sub*/info 120card*/pcm*/sub*/info
134 The general information of this PCM sub-stream. 121 The general information of this PCM sub-stream.
135 122
diff --git a/sound/core/Makefile b/sound/core/Makefile
index 394a38909f6b..4daf2f58261c 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -14,6 +14,9 @@ snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
14 pcm_memory.o memalloc.o 14 pcm_memory.o memalloc.o
15snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o 15snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o
16 16
17# for trace-points
18CFLAGS_pcm_lib.o := -I$(src)
19
17snd-pcm-dmaengine-objs := pcm_dmaengine.o 20snd-pcm-dmaengine-objs := pcm_dmaengine.o
18 21
19snd-rawmidi-objs := rawmidi.o 22snd-rawmidi-objs := rawmidi.o
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 7b9e2fb35a86..ec9e7866177f 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -32,6 +32,15 @@
32#include <sound/pcm_params.h> 32#include <sound/pcm_params.h>
33#include <sound/timer.h> 33#include <sound/timer.h>
34 34
35#ifdef CONFIG_SND_PCM_XRUN_DEBUG
36#define CREATE_TRACE_POINTS
37#include "pcm_trace.h"
38#else
39#define trace_hwptr(substream, pos, in_interrupt)
40#define trace_xrun(substream)
41#define trace_hw_ptr_error(substream, reason)
42#endif
43
35/* 44/*
36 * fill ring buffer with silence 45 * fill ring buffer with silence
37 * runtime->silence_start: starting pointer to silence area 46 * runtime->silence_start: starting pointer to silence area
@@ -146,10 +155,6 @@ EXPORT_SYMBOL(snd_pcm_debug_name);
146#define XRUN_DEBUG_BASIC (1<<0) 155#define XRUN_DEBUG_BASIC (1<<0)
147#define XRUN_DEBUG_STACK (1<<1) /* dump also stack */ 156#define XRUN_DEBUG_STACK (1<<1) /* dump also stack */
148#define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */ 157#define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */
149#define XRUN_DEBUG_PERIODUPDATE (1<<3) /* full period update info */
150#define XRUN_DEBUG_HWPTRUPDATE (1<<4) /* full hwptr update info */
151#define XRUN_DEBUG_LOG (1<<5) /* show last 10 positions on err */
152#define XRUN_DEBUG_LOGONCE (1<<6) /* do above only once */
153 158
154#ifdef CONFIG_SND_PCM_XRUN_DEBUG 159#ifdef CONFIG_SND_PCM_XRUN_DEBUG
155 160
@@ -168,6 +173,7 @@ static void xrun(struct snd_pcm_substream *substream)
168{ 173{
169 struct snd_pcm_runtime *runtime = substream->runtime; 174 struct snd_pcm_runtime *runtime = substream->runtime;
170 175
176 trace_xrun(substream);
171 if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) 177 if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
172 snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); 178 snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
173 snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); 179 snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
@@ -180,97 +186,19 @@ static void xrun(struct snd_pcm_substream *substream)
180} 186}
181 187
182#ifdef CONFIG_SND_PCM_XRUN_DEBUG 188#ifdef CONFIG_SND_PCM_XRUN_DEBUG
183#define hw_ptr_error(substream, fmt, args...) \ 189#define hw_ptr_error(substream, in_interrupt, reason, fmt, args...) \
184 do { \ 190 do { \
191 trace_hw_ptr_error(substream, reason); \
185 if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \ 192 if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \
186 xrun_log_show(substream); \ 193 pr_err_ratelimited("ALSA: PCM: [%c] " reason ": " fmt, \
187 pr_err_ratelimited("ALSA: PCM: " fmt, ##args); \ 194 (in_interrupt) ? 'Q' : 'P', ##args); \
188 dump_stack_on_xrun(substream); \ 195 dump_stack_on_xrun(substream); \
189 } \ 196 } \
190 } while (0) 197 } while (0)
191 198
192#define XRUN_LOG_CNT 10
193
194struct hwptr_log_entry {
195 unsigned int in_interrupt;
196 unsigned long jiffies;
197 snd_pcm_uframes_t pos;
198 snd_pcm_uframes_t period_size;
199 snd_pcm_uframes_t buffer_size;
200 snd_pcm_uframes_t old_hw_ptr;
201 snd_pcm_uframes_t hw_ptr_base;
202};
203
204struct snd_pcm_hwptr_log {
205 unsigned int idx;
206 unsigned int hit: 1;
207 struct hwptr_log_entry entries[XRUN_LOG_CNT];
208};
209
210static void xrun_log(struct snd_pcm_substream *substream,
211 snd_pcm_uframes_t pos, int in_interrupt)
212{
213 struct snd_pcm_runtime *runtime = substream->runtime;
214 struct snd_pcm_hwptr_log *log = runtime->hwptr_log;
215 struct hwptr_log_entry *entry;
216
217 if (log == NULL) {
218 log = kzalloc(sizeof(*log), GFP_ATOMIC);
219 if (log == NULL)
220 return;
221 runtime->hwptr_log = log;
222 } else {
223 if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
224 return;
225 }
226 entry = &log->entries[log->idx];
227 entry->in_interrupt = in_interrupt;
228 entry->jiffies = jiffies;
229 entry->pos = pos;
230 entry->period_size = runtime->period_size;
231 entry->buffer_size = runtime->buffer_size;
232 entry->old_hw_ptr = runtime->status->hw_ptr;
233 entry->hw_ptr_base = runtime->hw_ptr_base;
234 log->idx = (log->idx + 1) % XRUN_LOG_CNT;
235}
236
237static void xrun_log_show(struct snd_pcm_substream *substream)
238{
239 struct snd_pcm_hwptr_log *log = substream->runtime->hwptr_log;
240 struct hwptr_log_entry *entry;
241 char name[16];
242 unsigned int idx;
243 int cnt;
244
245 if (log == NULL)
246 return;
247 if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
248 return;
249 snd_pcm_debug_name(substream, name, sizeof(name));
250 for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) {
251 entry = &log->entries[idx];
252 if (entry->period_size == 0)
253 break;
254 pr_info("hwptr log: %s: %sj=%lu, pos=%ld/%ld/%ld, "
255 "hwptr=%ld/%ld\n",
256 name, entry->in_interrupt ? "[Q] " : "",
257 entry->jiffies,
258 (unsigned long)entry->pos,
259 (unsigned long)entry->period_size,
260 (unsigned long)entry->buffer_size,
261 (unsigned long)entry->old_hw_ptr,
262 (unsigned long)entry->hw_ptr_base);
263 idx++;
264 idx %= XRUN_LOG_CNT;
265 }
266 log->hit = 1;
267}
268
269#else /* ! CONFIG_SND_PCM_XRUN_DEBUG */ 199#else /* ! CONFIG_SND_PCM_XRUN_DEBUG */
270 200
271#define hw_ptr_error(substream, fmt, args...) do { } while (0) 201#define hw_ptr_error(substream, fmt, args...) do { } while (0)
272#define xrun_log(substream, pos, in_interrupt) do { } while (0)
273#define xrun_log_show(substream) do { } while (0)
274 202
275#endif 203#endif
276 204
@@ -343,7 +271,6 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
343 if (printk_ratelimit()) { 271 if (printk_ratelimit()) {
344 char name[16]; 272 char name[16];
345 snd_pcm_debug_name(substream, name, sizeof(name)); 273 snd_pcm_debug_name(substream, name, sizeof(name));
346 xrun_log_show(substream);
347 pcm_err(substream->pcm, 274 pcm_err(substream->pcm,
348 "BUG: %s, pos = %ld, buffer size = %ld, period size = %ld\n", 275 "BUG: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
349 name, pos, runtime->buffer_size, 276 name, pos, runtime->buffer_size,
@@ -352,8 +279,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
352 pos = 0; 279 pos = 0;
353 } 280 }
354 pos -= pos % runtime->min_align; 281 pos -= pos % runtime->min_align;
355 if (xrun_debug(substream, XRUN_DEBUG_LOG)) 282 trace_hwptr(substream, pos, in_interrupt);
356 xrun_log(substream, pos, in_interrupt);
357 hw_base = runtime->hw_ptr_base; 283 hw_base = runtime->hw_ptr_base;
358 new_hw_ptr = hw_base + pos; 284 new_hw_ptr = hw_base + pos;
359 if (in_interrupt) { 285 if (in_interrupt) {
@@ -388,22 +314,6 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
388 delta = new_hw_ptr - old_hw_ptr; 314 delta = new_hw_ptr - old_hw_ptr;
389 if (delta < 0) 315 if (delta < 0)
390 delta += runtime->boundary; 316 delta += runtime->boundary;
391 if (xrun_debug(substream, in_interrupt ?
392 XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) {
393 char name[16];
394 snd_pcm_debug_name(substream, name, sizeof(name));
395 pcm_dbg(substream->pcm,
396 "%s_update: %s: pos=%u/%u/%u, hwptr=%ld/%ld/%ld/%ld\n",
397 in_interrupt ? "period" : "hwptr",
398 name,
399 (unsigned int)pos,
400 (unsigned int)runtime->period_size,
401 (unsigned int)runtime->buffer_size,
402 (unsigned long)delta,
403 (unsigned long)old_hw_ptr,
404 (unsigned long)new_hw_ptr,
405 (unsigned long)runtime->hw_ptr_base);
406 }
407 317
408 if (runtime->no_period_wakeup) { 318 if (runtime->no_period_wakeup) {
409 snd_pcm_sframes_t xrun_threshold; 319 snd_pcm_sframes_t xrun_threshold;
@@ -431,13 +341,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
431 341
432 /* something must be really wrong */ 342 /* something must be really wrong */
433 if (delta >= runtime->buffer_size + runtime->period_size) { 343 if (delta >= runtime->buffer_size + runtime->period_size) {
434 hw_ptr_error(substream, 344 hw_ptr_error(substream, in_interrupt, "Unexpected hw_ptr",
435 "Unexpected hw_pointer value %s" 345 "(stream=%i, pos=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n",
436 "(stream=%i, pos=%ld, new_hw_ptr=%ld, " 346 substream->stream, (long)pos,
437 "old_hw_ptr=%ld)\n", 347 (long)new_hw_ptr, (long)old_hw_ptr);
438 in_interrupt ? "[Q] " : "[P]",
439 substream->stream, (long)pos,
440 (long)new_hw_ptr, (long)old_hw_ptr);
441 return 0; 348 return 0;
442 } 349 }
443 350
@@ -474,11 +381,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
474 delta--; 381 delta--;
475 } 382 }
476 /* align hw_base to buffer_size */ 383 /* align hw_base to buffer_size */
477 hw_ptr_error(substream, 384 hw_ptr_error(substream, in_interrupt, "hw_ptr skipping",
478 "hw_ptr skipping! %s" 385 "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n",
479 "(pos=%ld, delta=%ld, period=%ld, "
480 "jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n",
481 in_interrupt ? "[Q] " : "",
482 (long)pos, (long)hdelta, 386 (long)pos, (long)hdelta,
483 (long)runtime->period_size, jdelta, 387 (long)runtime->period_size, jdelta,
484 ((hdelta * HZ) / runtime->rate), hw_base, 388 ((hdelta * HZ) / runtime->rate), hw_base,
@@ -490,11 +394,9 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
490 } 394 }
491 no_jiffies_check: 395 no_jiffies_check:
492 if (delta > runtime->period_size + runtime->period_size / 2) { 396 if (delta > runtime->period_size + runtime->period_size / 2) {
493 hw_ptr_error(substream, 397 hw_ptr_error(substream, in_interrupt,
494 "Lost interrupts? %s" 398 "Lost interrupts?",
495 "(stream=%i, delta=%ld, new_hw_ptr=%ld, " 399 "(stream=%i, delta=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n",
496 "old_hw_ptr=%ld)\n",
497 in_interrupt ? "[Q] " : "",
498 substream->stream, (long)delta, 400 substream->stream, (long)delta,
499 (long)new_hw_ptr, 401 (long)new_hw_ptr,
500 (long)old_hw_ptr); 402 (long)old_hw_ptr);
diff --git a/sound/core/pcm_trace.h b/sound/core/pcm_trace.h
new file mode 100644
index 000000000000..b63b654da5ff
--- /dev/null
+++ b/sound/core/pcm_trace.h
@@ -0,0 +1,110 @@
1#undef TRACE_SYSTEM
2#define TRACE_SYSTEM snd_pcm
3#define TRACE_INCLUDE_FILE pcm_trace
4
5#if !defined(_PCM_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
6#define _PCM_TRACE_H
7
8#include <linux/tracepoint.h>
9
10TRACE_EVENT(hwptr,
11 TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_uframes_t pos, bool irq),
12 TP_ARGS(substream, pos, irq),
13 TP_STRUCT__entry(
14 __field( bool, in_interrupt )
15 __field( unsigned int, card )
16 __field( unsigned int, device )
17 __field( unsigned int, number )
18 __field( unsigned int, stream )
19 __field( snd_pcm_uframes_t, pos )
20 __field( snd_pcm_uframes_t, period_size )
21 __field( snd_pcm_uframes_t, buffer_size )
22 __field( snd_pcm_uframes_t, old_hw_ptr )
23 __field( snd_pcm_uframes_t, hw_ptr_base )
24 ),
25 TP_fast_assign(
26 __entry->in_interrupt = (irq);
27 __entry->card = (substream)->pcm->card->number;
28 __entry->device = (substream)->pcm->device;
29 __entry->number = (substream)->number;
30 __entry->stream = (substream)->stream;
31 __entry->pos = (pos);
32 __entry->period_size = (substream)->runtime->period_size;
33 __entry->buffer_size = (substream)->runtime->buffer_size;
34 __entry->old_hw_ptr = (substream)->runtime->status->hw_ptr;
35 __entry->hw_ptr_base = (substream)->runtime->hw_ptr_base;
36 ),
37 TP_printk("pcmC%dD%d%c/sub%d: %s: pos=%lu, old=%lu, base=%lu, period=%lu, buf=%lu",
38 __entry->card, __entry->device,
39 __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c',
40 __entry->number,
41 __entry->in_interrupt ? "IRQ" : "POS",
42 (unsigned long)__entry->pos,
43 (unsigned long)__entry->old_hw_ptr,
44 (unsigned long)__entry->hw_ptr_base,
45 (unsigned long)__entry->period_size,
46 (unsigned long)__entry->buffer_size)
47);
48
49TRACE_EVENT(xrun,
50 TP_PROTO(struct snd_pcm_substream *substream),
51 TP_ARGS(substream),
52 TP_STRUCT__entry(
53 __field( unsigned int, card )
54 __field( unsigned int, device )
55 __field( unsigned int, number )
56 __field( unsigned int, stream )
57 __field( snd_pcm_uframes_t, period_size )
58 __field( snd_pcm_uframes_t, buffer_size )
59 __field( snd_pcm_uframes_t, old_hw_ptr )
60 __field( snd_pcm_uframes_t, hw_ptr_base )
61 ),
62 TP_fast_assign(
63 __entry->card = (substream)->pcm->card->number;
64 __entry->device = (substream)->pcm->device;
65 __entry->number = (substream)->number;
66 __entry->stream = (substream)->stream;
67 __entry->period_size = (substream)->runtime->period_size;
68 __entry->buffer_size = (substream)->runtime->buffer_size;
69 __entry->old_hw_ptr = (substream)->runtime->status->hw_ptr;
70 __entry->hw_ptr_base = (substream)->runtime->hw_ptr_base;
71 ),
72 TP_printk("pcmC%dD%d%c/sub%d: XRUN: old=%lu, base=%lu, period=%lu, buf=%lu",
73 __entry->card, __entry->device,
74 __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c',
75 __entry->number,
76 (unsigned long)__entry->old_hw_ptr,
77 (unsigned long)__entry->hw_ptr_base,
78 (unsigned long)__entry->period_size,
79 (unsigned long)__entry->buffer_size)
80);
81
82TRACE_EVENT(hw_ptr_error,
83 TP_PROTO(struct snd_pcm_substream *substream, const char *why),
84 TP_ARGS(substream, why),
85 TP_STRUCT__entry(
86 __field( unsigned int, card )
87 __field( unsigned int, device )
88 __field( unsigned int, number )
89 __field( unsigned int, stream )
90 __field( const char *, reason )
91 ),
92 TP_fast_assign(
93 __entry->card = (substream)->pcm->card->number;
94 __entry->device = (substream)->pcm->device;
95 __entry->number = (substream)->number;
96 __entry->stream = (substream)->stream;
97 __entry->reason = (why);
98 ),
99 TP_printk("pcmC%dD%d%c/sub%d: ERROR: %s",
100 __entry->card, __entry->device,
101 __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c',
102 __entry->number, __entry->reason)
103);
104
105#endif /* _PCM_TRACE_H */
106
107/* This part must be outside protection */
108#undef TRACE_INCLUDE_PATH
109#define TRACE_INCLUDE_PATH .
110#include <trace/define_trace.h>