mbed os with nrf51 internal bandgap enabled to read battery level
Dependents: BLE_file_test BLE_Blink ExternalEncoder
TESTS/mbed_drivers/mem_trace/main.cpp@0:f269e3021894, 2016-10-23 (annotated)
- Committer:
- elessair
- Date:
- Sun Oct 23 15:10:02 2016 +0000
- Revision:
- 0:f269e3021894
Initial commit
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
elessair | 0:f269e3021894 | 1 | /* |
elessair | 0:f269e3021894 | 2 | * Copyright (c) 2013-2016, ARM Limited, All Rights Reserved |
elessair | 0:f269e3021894 | 3 | * SPDX-License-Identifier: Apache-2.0 |
elessair | 0:f269e3021894 | 4 | * |
elessair | 0:f269e3021894 | 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may |
elessair | 0:f269e3021894 | 6 | * not use this file except in compliance with the License. |
elessair | 0:f269e3021894 | 7 | * You may obtain a copy of the License at |
elessair | 0:f269e3021894 | 8 | * |
elessair | 0:f269e3021894 | 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
elessair | 0:f269e3021894 | 10 | * |
elessair | 0:f269e3021894 | 11 | * Unless required by applicable law or agreed to in writing, software |
elessair | 0:f269e3021894 | 12 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
elessair | 0:f269e3021894 | 13 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
elessair | 0:f269e3021894 | 14 | * See the License for the specific language governing permissions and |
elessair | 0:f269e3021894 | 15 | * limitations under the License. |
elessair | 0:f269e3021894 | 16 | */ |
elessair | 0:f269e3021894 | 17 | |
elessair | 0:f269e3021894 | 18 | #include "mbed.h" |
elessair | 0:f269e3021894 | 19 | #include "greentea-client/test_env.h" |
elessair | 0:f269e3021894 | 20 | #include "unity/unity.h" |
elessair | 0:f269e3021894 | 21 | #include "utest/utest.h" |
elessair | 0:f269e3021894 | 22 | #include "mbed_mem_trace.h" |
elessair | 0:f269e3021894 | 23 | #include <stdlib.h> |
elessair | 0:f269e3021894 | 24 | #include <stdio.h> |
elessair | 0:f269e3021894 | 25 | #include <stdarg.h> |
elessair | 0:f269e3021894 | 26 | |
elessair | 0:f269e3021894 | 27 | #ifndef MBED_MEM_TRACING_ENABLED |
elessair | 0:f269e3021894 | 28 | #error [NOT_SUPPORTED] test not supported |
elessair | 0:f269e3021894 | 29 | #endif |
elessair | 0:f269e3021894 | 30 | |
elessair | 0:f269e3021894 | 31 | using namespace utest::v1; |
elessair | 0:f269e3021894 | 32 | |
elessair | 0:f269e3021894 | 33 | /******************************************************************************/ |
elessair | 0:f269e3021894 | 34 | /* Helper functions and data structures */ |
elessair | 0:f269e3021894 | 35 | /******************************************************************************/ |
elessair | 0:f269e3021894 | 36 | |
elessair | 0:f269e3021894 | 37 | // This structure keeps data about the various memory allocation operations, |
elessair | 0:f269e3021894 | 38 | // as traced by 'test_trace_cb' below. |
elessair | 0:f269e3021894 | 39 | #define TEST_MAX_MEMORY_OPS 10 |
elessair | 0:f269e3021894 | 40 | // Trace results for all possible operations |
elessair | 0:f269e3021894 | 41 | typedef struct { |
elessair | 0:f269e3021894 | 42 | uint8_t op; |
elessair | 0:f269e3021894 | 43 | void *res; |
elessair | 0:f269e3021894 | 44 | union { |
elessair | 0:f269e3021894 | 45 | struct { |
elessair | 0:f269e3021894 | 46 | size_t arg_size; |
elessair | 0:f269e3021894 | 47 | } malloc_info; |
elessair | 0:f269e3021894 | 48 | struct { |
elessair | 0:f269e3021894 | 49 | void *arg_ptr; |
elessair | 0:f269e3021894 | 50 | size_t arg_size; |
elessair | 0:f269e3021894 | 51 | } realloc_info; |
elessair | 0:f269e3021894 | 52 | struct { |
elessair | 0:f269e3021894 | 53 | size_t arg_nmemb; |
elessair | 0:f269e3021894 | 54 | size_t arg_size; |
elessair | 0:f269e3021894 | 55 | } calloc_info; |
elessair | 0:f269e3021894 | 56 | struct { |
elessair | 0:f269e3021894 | 57 | void *arg_ptr; |
elessair | 0:f269e3021894 | 58 | } free_info; |
elessair | 0:f269e3021894 | 59 | }; |
elessair | 0:f269e3021894 | 60 | } mem_trace_data_t; |
elessair | 0:f269e3021894 | 61 | // Memory operation statistics |
elessair | 0:f269e3021894 | 62 | typedef struct { |
elessair | 0:f269e3021894 | 63 | mem_trace_data_t op_data[TEST_MAX_MEMORY_OPS]; |
elessair | 0:f269e3021894 | 64 | uint32_t total_ops; |
elessair | 0:f269e3021894 | 65 | bool invalid_op, overflow; |
elessair | 0:f269e3021894 | 66 | } stats_t; |
elessair | 0:f269e3021894 | 67 | static stats_t stats; |
elessair | 0:f269e3021894 | 68 | |
elessair | 0:f269e3021894 | 69 | // Clear all the memory statistics |
elessair | 0:f269e3021894 | 70 | static void test_clear_stats() { |
elessair | 0:f269e3021894 | 71 | memset(&stats, 0, sizeof(stats)); |
elessair | 0:f269e3021894 | 72 | } |
elessair | 0:f269e3021894 | 73 | |
elessair | 0:f269e3021894 | 74 | // Memory tracer callback that records each operation in "stats" (above) |
elessair | 0:f269e3021894 | 75 | extern "C" void test_trace_cb(uint8_t op, void *res, void *caller, ...) { |
elessair | 0:f269e3021894 | 76 | va_list va; |
elessair | 0:f269e3021894 | 77 | mem_trace_data_t *pmem = stats.op_data + stats.total_ops; |
elessair | 0:f269e3021894 | 78 | |
elessair | 0:f269e3021894 | 79 | if (stats.total_ops >= TEST_MAX_MEMORY_OPS) { |
elessair | 0:f269e3021894 | 80 | stats.overflow = true; |
elessair | 0:f269e3021894 | 81 | return; |
elessair | 0:f269e3021894 | 82 | } |
elessair | 0:f269e3021894 | 83 | va_start(va, caller); |
elessair | 0:f269e3021894 | 84 | pmem->op = op; |
elessair | 0:f269e3021894 | 85 | pmem->res = res; |
elessair | 0:f269e3021894 | 86 | switch(op) { |
elessair | 0:f269e3021894 | 87 | case MBED_MEM_TRACE_MALLOC: |
elessair | 0:f269e3021894 | 88 | pmem->malloc_info.arg_size = va_arg(va, size_t); |
elessair | 0:f269e3021894 | 89 | break; |
elessair | 0:f269e3021894 | 90 | |
elessair | 0:f269e3021894 | 91 | case MBED_MEM_TRACE_REALLOC: |
elessair | 0:f269e3021894 | 92 | pmem->realloc_info.arg_ptr = va_arg(va, void *); |
elessair | 0:f269e3021894 | 93 | pmem->realloc_info.arg_size = va_arg(va, size_t); |
elessair | 0:f269e3021894 | 94 | break; |
elessair | 0:f269e3021894 | 95 | |
elessair | 0:f269e3021894 | 96 | case MBED_MEM_TRACE_CALLOC: |
elessair | 0:f269e3021894 | 97 | pmem->calloc_info.arg_nmemb = va_arg(va, size_t); |
elessair | 0:f269e3021894 | 98 | pmem->calloc_info.arg_size = va_arg(va, size_t); |
elessair | 0:f269e3021894 | 99 | break; |
elessair | 0:f269e3021894 | 100 | |
elessair | 0:f269e3021894 | 101 | case MBED_MEM_TRACE_FREE: |
elessair | 0:f269e3021894 | 102 | pmem->free_info.arg_ptr = va_arg(va, void *); |
elessair | 0:f269e3021894 | 103 | break; |
elessair | 0:f269e3021894 | 104 | |
elessair | 0:f269e3021894 | 105 | default: |
elessair | 0:f269e3021894 | 106 | stats.invalid_op = true; |
elessair | 0:f269e3021894 | 107 | } |
elessair | 0:f269e3021894 | 108 | stats.total_ops ++; |
elessair | 0:f269e3021894 | 109 | va_end(va); |
elessair | 0:f269e3021894 | 110 | } |
elessair | 0:f269e3021894 | 111 | |
elessair | 0:f269e3021894 | 112 | // Generic sanity checks for the tracer |
elessair | 0:f269e3021894 | 113 | static void check_sanity(uint32_t expected_ops) { |
elessair | 0:f269e3021894 | 114 | TEST_ASSERT_FALSE(stats.overflow); |
elessair | 0:f269e3021894 | 115 | TEST_ASSERT_FALSE(stats.invalid_op); |
elessair | 0:f269e3021894 | 116 | TEST_ASSERT_EQUAL_UINT32(stats.total_ops, expected_ops); |
elessair | 0:f269e3021894 | 117 | } |
elessair | 0:f269e3021894 | 118 | |
elessair | 0:f269e3021894 | 119 | // Check a "malloc" operation |
elessair | 0:f269e3021894 | 120 | static void check_malloc_op(const mem_trace_data_t *p, void *expected_res, size_t expected_arg_size) { |
elessair | 0:f269e3021894 | 121 | TEST_ASSERT_EQUAL_UINT8(p->op, MBED_MEM_TRACE_MALLOC); |
elessair | 0:f269e3021894 | 122 | TEST_ASSERT_EQUAL_PTR(p->res, expected_res); |
elessair | 0:f269e3021894 | 123 | TEST_ASSERT_EQUAL_UINT32(p->malloc_info.arg_size, expected_arg_size); |
elessair | 0:f269e3021894 | 124 | } |
elessair | 0:f269e3021894 | 125 | |
elessair | 0:f269e3021894 | 126 | // Check a "free" operation |
elessair | 0:f269e3021894 | 127 | static void check_free_op(const mem_trace_data_t *p, void *expected_arg_ptr) { |
elessair | 0:f269e3021894 | 128 | TEST_ASSERT_EQUAL_UINT8(p->op, MBED_MEM_TRACE_FREE); |
elessair | 0:f269e3021894 | 129 | TEST_ASSERT_EQUAL_PTR(p->free_info.arg_ptr, expected_arg_ptr); |
elessair | 0:f269e3021894 | 130 | } |
elessair | 0:f269e3021894 | 131 | |
elessair | 0:f269e3021894 | 132 | // Check a "realloc" operation |
elessair | 0:f269e3021894 | 133 | static void check_realloc_op(const mem_trace_data_t *p, void *expected_res, void *expected_arg_ptr, size_t expected_arg_size) { |
elessair | 0:f269e3021894 | 134 | TEST_ASSERT_EQUAL_UINT8(p->op, MBED_MEM_TRACE_REALLOC); |
elessair | 0:f269e3021894 | 135 | TEST_ASSERT_EQUAL_PTR(p->res, expected_res); |
elessair | 0:f269e3021894 | 136 | TEST_ASSERT_EQUAL_UINT32(p->realloc_info.arg_ptr, expected_arg_ptr); |
elessair | 0:f269e3021894 | 137 | TEST_ASSERT_EQUAL_UINT32(p->realloc_info.arg_size, expected_arg_size); |
elessair | 0:f269e3021894 | 138 | } |
elessair | 0:f269e3021894 | 139 | |
elessair | 0:f269e3021894 | 140 | // Check a "calloc" operation |
elessair | 0:f269e3021894 | 141 | static void check_calloc_op(const mem_trace_data_t *p, void *expected_res, size_t expected_arg_nmemb, size_t expected_arg_size) { |
elessair | 0:f269e3021894 | 142 | TEST_ASSERT_EQUAL_UINT8(p->op, MBED_MEM_TRACE_CALLOC); |
elessair | 0:f269e3021894 | 143 | TEST_ASSERT_EQUAL_PTR(p->res, expected_res); |
elessair | 0:f269e3021894 | 144 | TEST_ASSERT_EQUAL_UINT32(p->calloc_info.arg_nmemb, expected_arg_nmemb); |
elessair | 0:f269e3021894 | 145 | TEST_ASSERT_EQUAL_UINT32(p->calloc_info.arg_size, expected_arg_size); |
elessair | 0:f269e3021894 | 146 | } |
elessair | 0:f269e3021894 | 147 | |
elessair | 0:f269e3021894 | 148 | /******************************************************************************/ |
elessair | 0:f269e3021894 | 149 | /* Tests */ |
elessair | 0:f269e3021894 | 150 | /******************************************************************************/ |
elessair | 0:f269e3021894 | 151 | |
elessair | 0:f269e3021894 | 152 | // Allocate a single buffer, then free it. Check that tracing matches the operations. |
elessair | 0:f269e3021894 | 153 | static void test_case_single_malloc_free() { |
elessair | 0:f269e3021894 | 154 | const size_t block_size = 126; |
elessair | 0:f269e3021894 | 155 | const mem_trace_data_t *pmem = stats.op_data; |
elessair | 0:f269e3021894 | 156 | |
elessair | 0:f269e3021894 | 157 | test_clear_stats(); |
elessair | 0:f269e3021894 | 158 | mbed_mem_trace_set_callback(test_trace_cb); |
elessair | 0:f269e3021894 | 159 | // Allocate a single memory block |
elessair | 0:f269e3021894 | 160 | void *p = malloc(block_size); |
elessair | 0:f269e3021894 | 161 | TEST_ASSERT_NOT_EQUAL(p, NULL); |
elessair | 0:f269e3021894 | 162 | // Free the memory block |
elessair | 0:f269e3021894 | 163 | free(p); |
elessair | 0:f269e3021894 | 164 | // Stop tracing |
elessair | 0:f269e3021894 | 165 | mbed_mem_trace_set_callback(NULL); |
elessair | 0:f269e3021894 | 166 | // Check tracer result |
elessair | 0:f269e3021894 | 167 | check_sanity(2); |
elessair | 0:f269e3021894 | 168 | check_malloc_op(pmem ++, p, block_size); |
elessair | 0:f269e3021894 | 169 | check_free_op(pmem, p); |
elessair | 0:f269e3021894 | 170 | } |
elessair | 0:f269e3021894 | 171 | |
elessair | 0:f269e3021894 | 172 | // Test all memory operations (malloc, realloc, free, calloc) |
elessair | 0:f269e3021894 | 173 | static void test_case_all_memory_ops() { |
elessair | 0:f269e3021894 | 174 | const size_t malloc_size = 40, realloc_size = 80, nmemb = 25, size = 10; |
elessair | 0:f269e3021894 | 175 | const mem_trace_data_t *pmem = stats.op_data; |
elessair | 0:f269e3021894 | 176 | |
elessair | 0:f269e3021894 | 177 | test_clear_stats(); |
elessair | 0:f269e3021894 | 178 | mbed_mem_trace_set_callback(test_trace_cb); |
elessair | 0:f269e3021894 | 179 | // Allocate a single memory block, the realloc it |
elessair | 0:f269e3021894 | 180 | void *p_malloc = malloc(malloc_size); |
elessair | 0:f269e3021894 | 181 | TEST_ASSERT_NOT_EQUAL(p_malloc, NULL); |
elessair | 0:f269e3021894 | 182 | void *p_realloc = realloc(p_malloc, realloc_size); |
elessair | 0:f269e3021894 | 183 | TEST_ASSERT_NOT_EQUAL(p_realloc, NULL); |
elessair | 0:f269e3021894 | 184 | // Use calloc() now |
elessair | 0:f269e3021894 | 185 | void *p_calloc = calloc(nmemb, size); |
elessair | 0:f269e3021894 | 186 | //TEST_ASSERT_NOT_EQUAL(p_calloc, NULL); |
elessair | 0:f269e3021894 | 187 | // Free the realloc() pointer first, then the calloc() one |
elessair | 0:f269e3021894 | 188 | free(p_realloc); |
elessair | 0:f269e3021894 | 189 | free(p_calloc); |
elessair | 0:f269e3021894 | 190 | // Stop tracing |
elessair | 0:f269e3021894 | 191 | mbed_mem_trace_set_callback(NULL); |
elessair | 0:f269e3021894 | 192 | // Check tracer result |
elessair | 0:f269e3021894 | 193 | check_sanity(6); |
elessair | 0:f269e3021894 | 194 | check_malloc_op(pmem ++, p_malloc, malloc_size); |
elessair | 0:f269e3021894 | 195 | check_realloc_op(pmem ++, p_realloc, p_malloc, realloc_size); |
elessair | 0:f269e3021894 | 196 | // calloc() calls malloc() internally |
elessair | 0:f269e3021894 | 197 | check_malloc_op(pmem ++, p_calloc, nmemb * size); |
elessair | 0:f269e3021894 | 198 | check_calloc_op(pmem ++, p_calloc, nmemb, size); |
elessair | 0:f269e3021894 | 199 | check_free_op(pmem ++, p_realloc); |
elessair | 0:f269e3021894 | 200 | check_free_op(pmem, p_calloc); |
elessair | 0:f269e3021894 | 201 | } |
elessair | 0:f269e3021894 | 202 | |
elessair | 0:f269e3021894 | 203 | // Test that tracing is off when using a NULL callback |
elessair | 0:f269e3021894 | 204 | static void test_case_trace_off() { |
elessair | 0:f269e3021894 | 205 | const size_t malloc_size = 10; |
elessair | 0:f269e3021894 | 206 | |
elessair | 0:f269e3021894 | 207 | test_clear_stats(); |
elessair | 0:f269e3021894 | 208 | // We don't want any tracing |
elessair | 0:f269e3021894 | 209 | mbed_mem_trace_set_callback(NULL); |
elessair | 0:f269e3021894 | 210 | // Allocate a buffer and free it |
elessair | 0:f269e3021894 | 211 | void *p_malloc = malloc(malloc_size); |
elessair | 0:f269e3021894 | 212 | TEST_ASSERT_NOT_EQUAL(p_malloc, NULL); |
elessair | 0:f269e3021894 | 213 | free(p_malloc); |
elessair | 0:f269e3021894 | 214 | // Check that we didn't trace anything |
elessair | 0:f269e3021894 | 215 | check_sanity(0); |
elessair | 0:f269e3021894 | 216 | } |
elessair | 0:f269e3021894 | 217 | |
elessair | 0:f269e3021894 | 218 | // Test partial tracing (start tracing, stop tracing, restart later) |
elessair | 0:f269e3021894 | 219 | static void test_case_partial_trace() { |
elessair | 0:f269e3021894 | 220 | const size_t malloc_size_1 = 20, malloc_size_2 = 30; |
elessair | 0:f269e3021894 | 221 | const mem_trace_data_t *pmem = stats.op_data; |
elessair | 0:f269e3021894 | 222 | |
elessair | 0:f269e3021894 | 223 | test_clear_stats(); |
elessair | 0:f269e3021894 | 224 | // Start tracing |
elessair | 0:f269e3021894 | 225 | mbed_mem_trace_set_callback(test_trace_cb); |
elessair | 0:f269e3021894 | 226 | // Allocate a buffer |
elessair | 0:f269e3021894 | 227 | void *p_malloc_1 = malloc(malloc_size_1); |
elessair | 0:f269e3021894 | 228 | TEST_ASSERT_NOT_EQUAL(p_malloc_1, NULL); |
elessair | 0:f269e3021894 | 229 | // Disable tracing before freeing the first buffer |
elessair | 0:f269e3021894 | 230 | mbed_mem_trace_set_callback(NULL); |
elessair | 0:f269e3021894 | 231 | free(p_malloc_1); |
elessair | 0:f269e3021894 | 232 | // Allocate another buffer (still not traced) |
elessair | 0:f269e3021894 | 233 | void *p_malloc_2 = malloc(malloc_size_2); |
elessair | 0:f269e3021894 | 234 | TEST_ASSERT_NOT_EQUAL(p_malloc_2, NULL); |
elessair | 0:f269e3021894 | 235 | // Re-enable tracing |
elessair | 0:f269e3021894 | 236 | mbed_mem_trace_set_callback(test_trace_cb); |
elessair | 0:f269e3021894 | 237 | // And free the second buffer (this operation should be tracer) |
elessair | 0:f269e3021894 | 238 | free(p_malloc_2); |
elessair | 0:f269e3021894 | 239 | // Stop tracing |
elessair | 0:f269e3021894 | 240 | mbed_mem_trace_set_callback(NULL); |
elessair | 0:f269e3021894 | 241 | // Check tracer result |
elessair | 0:f269e3021894 | 242 | check_sanity(2); |
elessair | 0:f269e3021894 | 243 | check_malloc_op(pmem ++, p_malloc_1, malloc_size_1); |
elessair | 0:f269e3021894 | 244 | check_free_op(pmem, p_malloc_2); |
elessair | 0:f269e3021894 | 245 | } |
elessair | 0:f269e3021894 | 246 | |
elessair | 0:f269e3021894 | 247 | // Test new/delete tracing |
elessair | 0:f269e3021894 | 248 | static void test_case_new_delete() { |
elessair | 0:f269e3021894 | 249 | const mem_trace_data_t *pmem = stats.op_data; |
elessair | 0:f269e3021894 | 250 | |
elessair | 0:f269e3021894 | 251 | test_clear_stats(); |
elessair | 0:f269e3021894 | 252 | // Start tracing |
elessair | 0:f269e3021894 | 253 | mbed_mem_trace_set_callback(test_trace_cb); |
elessair | 0:f269e3021894 | 254 | // Test new, new[], delete and delete[] |
elessair | 0:f269e3021894 | 255 | int *p_int = new int; |
elessair | 0:f269e3021894 | 256 | int *p_int_array = new int[10]; |
elessair | 0:f269e3021894 | 257 | delete p_int; |
elessair | 0:f269e3021894 | 258 | delete[] p_int_array; |
elessair | 0:f269e3021894 | 259 | // Stop tracing |
elessair | 0:f269e3021894 | 260 | mbed_mem_trace_set_callback(NULL); |
elessair | 0:f269e3021894 | 261 | // Check tracer result |
elessair | 0:f269e3021894 | 262 | check_sanity(4); |
elessair | 0:f269e3021894 | 263 | check_malloc_op(pmem ++, p_int, sizeof(int)); |
elessair | 0:f269e3021894 | 264 | check_malloc_op(pmem ++, p_int_array, 10 * sizeof(int)); |
elessair | 0:f269e3021894 | 265 | check_free_op(pmem ++, p_int); |
elessair | 0:f269e3021894 | 266 | check_free_op(pmem ++, p_int_array); |
elessair | 0:f269e3021894 | 267 | } |
elessair | 0:f269e3021894 | 268 | |
elessair | 0:f269e3021894 | 269 | static Case cases[] = { |
elessair | 0:f269e3021894 | 270 | Case("single malloc/free", test_case_single_malloc_free), |
elessair | 0:f269e3021894 | 271 | Case("all memory operations", test_case_all_memory_ops), |
elessair | 0:f269e3021894 | 272 | Case("trace off", test_case_trace_off), |
elessair | 0:f269e3021894 | 273 | Case("partial trace", test_case_partial_trace), |
elessair | 0:f269e3021894 | 274 | Case("test new/delete", test_case_new_delete) |
elessair | 0:f269e3021894 | 275 | }; |
elessair | 0:f269e3021894 | 276 | |
elessair | 0:f269e3021894 | 277 | static status_t greentea_test_setup(const size_t number_of_cases) { |
elessair | 0:f269e3021894 | 278 | GREENTEA_SETUP(20, "default_auto"); |
elessair | 0:f269e3021894 | 279 | return greentea_test_setup_handler(number_of_cases); |
elessair | 0:f269e3021894 | 280 | } |
elessair | 0:f269e3021894 | 281 | |
elessair | 0:f269e3021894 | 282 | static Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); |
elessair | 0:f269e3021894 | 283 | |
elessair | 0:f269e3021894 | 284 | int main() { |
elessair | 0:f269e3021894 | 285 | // Disable stdout buffering to prevent any unwanted allocations |
elessair | 0:f269e3021894 | 286 | setvbuf(stdout, NULL, _IONBF, 0); |
elessair | 0:f269e3021894 | 287 | Harness::run(specification); |
elessair | 0:f269e3021894 | 288 | } |
elessair | 0:f269e3021894 | 289 |