| /* Copyright 2018 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include <stdbool.h> |
| #include <syslog.h> |
| #include "audio_thread.h" |
| #include "cras_iodev_list.h" |
| #include "cras_main_message.h" |
| #include "cras_system_state.h" |
| #include "cras_types.h" |
| #include "cras_util.h" |
| |
| #define MIN_WAIT_SECOND 30 |
| |
| struct cras_audio_thread_event_message { |
| struct cras_main_message header; |
| enum CRAS_AUDIO_THREAD_EVENT_TYPE event_type; |
| }; |
| |
| static void take_snapshot(enum CRAS_AUDIO_THREAD_EVENT_TYPE event_type) |
| { |
| struct cras_audio_thread_snapshot *snapshot; |
| |
| snapshot = (struct cras_audio_thread_snapshot *)calloc( |
| 1, sizeof(struct cras_audio_thread_snapshot)); |
| struct timespec now_time; |
| clock_gettime(CLOCK_MONOTONIC_RAW, &now_time); |
| snapshot->timestamp = now_time; |
| snapshot->event_type = event_type; |
| audio_thread_dump_thread_info(cras_iodev_list_get_audio_thread(), |
| &snapshot->audio_debug_info); |
| cras_system_state_add_snapshot(snapshot); |
| } |
| |
| static void cras_audio_thread_event_message_init( |
| struct cras_audio_thread_event_message *msg, |
| enum CRAS_AUDIO_THREAD_EVENT_TYPE event_type) |
| { |
| msg->header.type = CRAS_MAIN_AUDIO_THREAD_EVENT; |
| msg->header.length = sizeof(*msg); |
| msg->event_type = event_type; |
| } |
| |
| int cras_audio_thread_event_send(enum CRAS_AUDIO_THREAD_EVENT_TYPE event_type) |
| { |
| struct cras_audio_thread_event_message msg; |
| cras_audio_thread_event_message_init(&msg, event_type); |
| return cras_main_message_send(&msg.header); |
| } |
| |
| int cras_audio_thread_event_a2dp_overrun() |
| { |
| return cras_audio_thread_event_send(AUDIO_THREAD_EVENT_A2DP_OVERRUN); |
| } |
| |
| int cras_audio_thread_event_a2dp_throttle() |
| { |
| return cras_audio_thread_event_send(AUDIO_THREAD_EVENT_A2DP_THROTTLE); |
| } |
| |
| int cras_audio_thread_event_debug() |
| { |
| return cras_audio_thread_event_send(AUDIO_THREAD_EVENT_DEBUG); |
| } |
| |
| int cras_audio_thread_event_busyloop() |
| { |
| return cras_audio_thread_event_send(AUDIO_THREAD_EVENT_BUSYLOOP); |
| } |
| |
| int cras_audio_thread_event_underrun() |
| { |
| return cras_audio_thread_event_send(AUDIO_THREAD_EVENT_UNDERRUN); |
| } |
| |
| int cras_audio_thread_event_severe_underrun() |
| { |
| return cras_audio_thread_event_send(AUDIO_THREAD_EVENT_SEVERE_UNDERRUN); |
| } |
| |
| int cras_audio_thread_event_drop_samples() |
| { |
| return cras_audio_thread_event_send(AUDIO_THREAD_EVENT_DROP_SAMPLES); |
| } |
| |
| int cras_audio_thread_event_dev_overrun() |
| { |
| return cras_audio_thread_event_send(AUDIO_THREAD_EVENT_DEV_OVERRUN); |
| } |
| |
| static struct timespec last_event_snapshot_time[AUDIO_THREAD_EVENT_TYPE_COUNT]; |
| |
| /* |
| * Callback function for handling audio thread events in main thread, |
| * which takes a snapshot of the audio thread and waits at least 30 seconds |
| * for the same event type. Events with the same event type within 30 seconds |
| * will be ignored by the handle function. |
| */ |
| static void handle_audio_thread_event_message(struct cras_main_message *msg, |
| void *arg) |
| { |
| struct cras_audio_thread_event_message *audio_thread_msg = |
| (struct cras_audio_thread_event_message *)msg; |
| struct timespec now_time; |
| |
| /* |
| * Skip invalid event types |
| */ |
| if (audio_thread_msg->event_type >= AUDIO_THREAD_EVENT_TYPE_COUNT) |
| return; |
| |
| struct timespec *last_snapshot_time = |
| &last_event_snapshot_time[audio_thread_msg->event_type]; |
| |
| clock_gettime(CLOCK_REALTIME, &now_time); |
| |
| /* |
| * Wait at least 30 seconds for the same event type |
| */ |
| struct timespec diff_time; |
| subtract_timespecs(&now_time, last_snapshot_time, &diff_time); |
| if ((last_snapshot_time->tv_sec == 0 && |
| last_snapshot_time->tv_nsec == 0) || |
| diff_time.tv_sec >= MIN_WAIT_SECOND) { |
| take_snapshot(audio_thread_msg->event_type); |
| *last_snapshot_time = now_time; |
| } |
| } |
| |
| int cras_audio_thread_monitor_init() |
| { |
| memset(last_event_snapshot_time, 0, |
| sizeof(struct timespec) * AUDIO_THREAD_EVENT_TYPE_COUNT); |
| cras_main_message_add_handler(CRAS_MAIN_AUDIO_THREAD_EVENT, |
| handle_audio_thread_event_message, NULL); |
| return 0; |
| } |