1 /* 2 * Copyright (c) 2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 /* This files contains process dump ring buffer module. */ 17 18 #ifndef DFX_RING_BUFFER_H 19 #define DFX_RING_BUFFER_H 20 21 #include "dfx_ring_buffer_block.h" 22 23 /** 24 * @brief A ring buffer is a FIFO structure that can be used to 25 * spool data between devices. 26 * 27 * There is a Skip() function that allows the client to 28 * control when the read cursor is changed. This is so the 29 * client can perform an action after Read() without the 30 * write cursor overwriting data while the read block is used. 31 * 32 * For e.g with the sequence of events: 33 * 1. Read(1000, false) 34 * 2. Busy writing to sd card for 5 seconds 35 * 3. Skip() 36 * 37 * Because the skip isn't called until the writing 38 * has finished, another thread can .Append() without 39 * corrupting the data being written. 40 * 41 * 42 * @attention The ring buffer can only contain Length-1 number of entries, 43 * because the last index is reserved for overrun checks. 44 * 45 * @tparam Length The length of the backing store array. 46 * @tparam T The type of data stored. 47 */ 48 template<unsigned int LENGTH, class T> 49 class DfxRingBuffer { 50 public: DfxRingBuffer()51 DfxRingBuffer() : read_position(0), write_position(0), data{{T()}}, overrun_flag(false) 52 { 53 } 54 ~DfxRingBuffer()55 ~DfxRingBuffer() 56 { 57 } 58 59 /** 60 * @brief Appends a value the end of the 61 * buffer. 62 */ Append(T & value)63 void Append(T& value) 64 { 65 /* 66 * If the next position is where the read cursor 67 * is then we have a full buffer. 68 */ 69 bool buffer_full; 70 71 buffer_full = ((write_position + 1U) % LENGTH) == read_position; 72 73 if (buffer_full) { 74 /* 75 * Tried to append a value while the buffer is full. 76 */ 77 overrun_flag = true; 78 } else { 79 /* 80 * Buffer isn't full yet, write to the curr write position 81 * and increment it by 1. 82 */ 83 overrun_flag = false; 84 data[write_position] = value; 85 write_position = (write_position + 1U) % LENGTH; 86 } 87 } 88 89 /** 90 * @brief Retrieve a continuous block of 91 * valid buffered data. 92 * @param num_reads_requested How many reads are required. 93 * @return A block of items containing the maximum 94 * number the buffer can provide at this time. 95 */ Read(unsigned int num_reads_requested)96 DfxRingBufferBlock<T> Read(unsigned int num_reads_requested) 97 { 98 bool bridges_zero; 99 DfxRingBufferBlock<T> block; 100 101 /* 102 * Make sure the number of reads does not bridge the 0 index. 103 * This is because we can only provide 1 contiguous block at 104 * a time. 105 */ 106 bridges_zero = (read_position > write_position); 107 108 if (bridges_zero) { 109 unsigned int reads_to_end; 110 bool req_surpasses_buffer_end; 111 112 reads_to_end = LENGTH - read_position; 113 req_surpasses_buffer_end = num_reads_requested > reads_to_end; 114 115 if (req_surpasses_buffer_end) { 116 /* 117 * If the block requested exceeds the buffer end. Then 118 * return a block that reaches the end and no more. 119 */ 120 block.SetStart(&(data[read_position])); 121 block.SetLength(reads_to_end); 122 } else { 123 /* 124 * If the block requested does not exceed 0 125 * then return a block that reaches the number of reads required. 126 */ 127 block.SetStart(&(data[read_position])); 128 block.SetLength(num_reads_requested); 129 } 130 } else { 131 /* 132 * If the block doesn't bridge the zero then 133 * return the maximum number of reads to the write 134 * cursor. 135 */ 136 unsigned int max_num_reads; 137 unsigned int num_reads_to_write_position; 138 139 num_reads_to_write_position = (write_position - read_position); 140 141 if (num_reads_requested > num_reads_to_write_position) { 142 /* 143 * If the block length requested exceeds the 144 * number of items available, then restrict 145 * the block length to the distance to the write position. 146 */ 147 max_num_reads = num_reads_to_write_position; 148 } else { 149 /* 150 * If the block length requested does not exceed the 151 * number of items available then the entire 152 * block is valid. 153 */ 154 max_num_reads = num_reads_requested; 155 } 156 157 block.SetStart(&(data[read_position])); 158 block.SetLength(max_num_reads); 159 } 160 161 return block; 162 } 163 164 /** 165 * @brief Advances the read position. 166 * 167 */ Skip(unsigned int num_reads)168 void Skip(unsigned int num_reads) 169 { 170 read_position = (read_position + num_reads) % LENGTH; 171 } 172 Overrun()173 bool Overrun() 174 { 175 return overrun_flag; 176 } 177 178 /** 179 * @brief The total size of the ring buffer including the full position. 180 * 181 */ Length()182 unsigned int Length() 183 { 184 return LENGTH; 185 } 186 187 /** 188 * @brief Returns the number of reads available. 189 * 190 */ Available()191 unsigned int Available() 192 { 193 bool bridges_zero; 194 unsigned available_reads; 195 196 bridges_zero = read_position > write_position; 197 available_reads = 0; 198 199 if (bridges_zero) { 200 /* Add the number of reads to zero, and number of reads from 0 to the write cursor */ 201 unsigned int num_reads_to_zero; 202 unsigned int num_reads_to_write_position; 203 204 num_reads_to_zero = LENGTH - read_position; 205 num_reads_to_write_position = write_position; 206 207 available_reads = num_reads_to_zero + num_reads_to_write_position; 208 } else { 209 /* The number of available reads is between the write position and the read position. */ 210 available_reads = write_position - read_position; 211 } 212 213 return available_reads; 214 } 215 216 private: 217 volatile unsigned int read_position; 218 volatile unsigned int write_position; 219 220 T data[LENGTH] = {T()}; 221 222 bool overrun_flag = false; 223 }; 224 225 #endif 226