1 /*
2 * Copyright 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "le_audio_log_history.h"
18
19 #include <base/logging.h>
20
21 #include <cstdint>
22 #include <memory>
23 #include <string>
24
25 #include "gd/common/circular_buffer.h"
26 #include "gd/common/strings.h"
27 #include "main/shim/dumpsys.h"
28 #include "osi/include/log.h"
29 #include "osi/include/osi.h"
30 #include "osi/include/properties.h"
31
32 constexpr size_t kMaxLogSize = 255;
33 constexpr size_t kLeAudioLogHistoryBufferSize = 200;
34
35 class TimestampedStringCircularBuffer
36 : public bluetooth::common::TimestampedCircularBuffer<std::string> {
37 public:
TimestampedStringCircularBuffer(size_t size)38 explicit TimestampedStringCircularBuffer(size_t size)
39 : bluetooth::common::TimestampedCircularBuffer<std::string>(size) {}
40
Push(const std::string & s)41 void Push(const std::string& s) {
42 bluetooth::common::TimestampedCircularBuffer<std::string>::Push(
43 s.substr(0, kMaxLogSize));
44 }
45
46 template <typename... Args>
Push(Args...args)47 void Push(Args... args) {
48 char buf[kMaxLogSize];
49 std::snprintf(buf, sizeof(buf), args...);
50 bluetooth::common::TimestampedCircularBuffer<std::string>::Push(
51 std::string(buf));
52 }
53 };
54
55 class LeAudioLogHistoryImpl;
56 LeAudioLogHistoryImpl* instance;
57
58 constexpr size_t kMaxLogHistoryTagLength = 14;
59 constexpr size_t kMaxLogHistoryMsgLength = 44;
60 const std::string kTimeFormat("%Y-%m-%d %H:%M:%S");
61
62 using Record = bluetooth::common::TimestampedEntry<std::string>;
63
64 class LeAudioLogHistoryImpl : public LeAudioLogHistory {
65 public:
~LeAudioLogHistoryImpl(void)66 ~LeAudioLogHistoryImpl(void) { history_.reset(); }
67
LeAudioLogHistoryImpl(void)68 LeAudioLogHistoryImpl(void) {
69 history_ = std::make_shared<TimestampedStringCircularBuffer>(
70 kLeAudioLogHistoryBufferSize);
71 CHECK(history_ != nullptr);
72 history_->Push(std::string("Initialized le_audio history"));
73 }
74
Dump(int fd)75 void Dump(int fd) {
76 #define DUMPSYS_TAG "::le_audio"
77
78 LOG_DUMPSYS_TITLE(fd, DUMPSYS_TAG);
79 if (history_ == nullptr) {
80 return;
81 }
82 std::vector<Record> history = history_->Pull();
83 for (auto& record : history) {
84 time_t then = record.timestamp / 1000;
85 struct tm tm;
86 localtime_r(&then, &tm);
87 auto s2 = bluetooth::common::StringFormatTime(kTimeFormat, tm);
88 LOG_DUMPSYS(fd, " %s.%03u %s", s2.c_str(),
89 static_cast<unsigned int>(record.timestamp % 1000),
90 record.entry.c_str());
91 }
92 #undef DUMPSYS_TAG
93 }
94
AddLogHistory(const std::string & tag,int group_id,const RawAddress & addr,const std::string & msg,const std::string & extra)95 void AddLogHistory(const std::string& tag, int group_id,
96 const RawAddress& addr, const std::string& msg,
97 const std::string& extra) {
98 add_logs_history_common(tag, group_id, addr, msg, extra);
99 }
100
AddLogHistory(const std::string & tag,int group_id,const RawAddress & addr,const std::string & msg)101 void AddLogHistory(const std::string& tag, int group_id,
102 const RawAddress& addr, const std::string& msg) {
103 AddLogHistory(tag, group_id, addr, msg, std::string());
104 }
105
106 private:
add_logs_history_common(const std::string & tag,int group_id,const RawAddress & addr,const std::string & msg,const std::string & extra)107 void add_logs_history_common(const std::string& tag, int group_id,
108 const RawAddress& addr, const std::string& msg,
109 const std::string& extra) {
110 if (history_ == nullptr) {
111 LOG_ERROR(
112 "LeAudioLogHistory has not been constructed or already destroyed !");
113 return;
114 }
115
116 history_->Push("%-*s GID %-3d %-*s: %-22s %s", kMaxLogHistoryTagLength,
117 tag.substr(0, kMaxLogHistoryTagLength).c_str(), group_id,
118 kMaxLogHistoryMsgLength,
119 msg.substr(0, kMaxLogHistoryMsgLength).c_str(),
120 ADDRESS_TO_LOGGABLE_CSTR(addr), extra.c_str());
121 }
122
123 std::shared_ptr<TimestampedStringCircularBuffer> history_{nullptr};
124 };
125
Get(void)126 LeAudioLogHistory* LeAudioLogHistory::Get(void) {
127 if (instance) {
128 return instance;
129 }
130 instance = new LeAudioLogHistoryImpl();
131 return instance;
132 }
133
DebugDump(int fd)134 void LeAudioLogHistory::DebugDump(int fd) {
135 if (instance) {
136 instance->Dump(fd);
137 }
138 }
139
Cleanup(void)140 void LeAudioLogHistory::Cleanup(void) {
141 if (!instance) {
142 return;
143 }
144 auto ptr = instance;
145 instance = nullptr;
146 delete ptr;
147 }