Mark Salyzyn | 9568705 | 2014-10-02 11:12:28 -0700 | [diff] [blame] | 1 | /* |
| 2 | ** Copyright 2014, 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 <ctype.h> |
Mark Salyzyn | c158456 | 2015-03-12 15:46:29 -0700 | [diff] [blame] | 18 | #include <pthread.h> |
| 19 | #include <stdlib.h> |
Mark Salyzyn | 9568705 | 2014-10-02 11:12:28 -0700 | [diff] [blame] | 20 | #include <string.h> |
Mark Salyzyn | c158456 | 2015-03-12 15:46:29 -0700 | [diff] [blame] | 21 | #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ |
| 22 | #include <sys/_system_properties.h> |
Mark Salyzyn | 9568705 | 2014-10-02 11:12:28 -0700 | [diff] [blame] | 23 | |
| 24 | #include <android/log.h> |
| 25 | |
Mark Salyzyn | c158456 | 2015-03-12 15:46:29 -0700 | [diff] [blame] | 26 | struct cache { |
| 27 | const prop_info *pinfo; |
| 28 | uint32_t serial; |
| 29 | char c; |
| 30 | }; |
| 31 | |
| 32 | static void refresh_cache(struct cache *cache, const char *key) |
Mark Salyzyn | 9568705 | 2014-10-02 11:12:28 -0700 | [diff] [blame] | 33 | { |
Mark Salyzyn | c158456 | 2015-03-12 15:46:29 -0700 | [diff] [blame] | 34 | uint32_t serial; |
Mark Salyzyn | 9568705 | 2014-10-02 11:12:28 -0700 | [diff] [blame] | 35 | char buf[PROP_VALUE_MAX]; |
| 36 | |
Mark Salyzyn | c158456 | 2015-03-12 15:46:29 -0700 | [diff] [blame] | 37 | if (!cache->pinfo) { |
| 38 | cache->pinfo = __system_property_find(key); |
| 39 | if (!cache->pinfo) { |
| 40 | return; |
| 41 | } |
Mark Salyzyn | 9568705 | 2014-10-02 11:12:28 -0700 | [diff] [blame] | 42 | } |
Mark Salyzyn | c158456 | 2015-03-12 15:46:29 -0700 | [diff] [blame] | 43 | serial = __system_property_serial(cache->pinfo); |
| 44 | if (serial == cache->serial) { |
| 45 | return; |
| 46 | } |
| 47 | cache->serial = serial; |
| 48 | __system_property_read(cache->pinfo, 0, buf); |
| 49 | cache->c = buf[0]; |
| 50 | } |
Mark Salyzyn | 9568705 | 2014-10-02 11:12:28 -0700 | [diff] [blame] | 51 | |
Mark Salyzyn | c158456 | 2015-03-12 15:46:29 -0700 | [diff] [blame] | 52 | static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; |
| 53 | |
| 54 | static int __android_log_level(const char *tag, int def) |
| 55 | { |
| 56 | /* sizeof() is used on this array below */ |
| 57 | static const char log_namespace[] = "persist.log.tag."; |
| 58 | static const size_t base_offset = 8; /* skip "persist." */ |
| 59 | /* calculate the size of our key temporary buffer */ |
| 60 | const size_t taglen = (tag && *tag) ? strlen(tag) : 0; |
| 61 | /* sizeof(log_namespace) = strlen(log_namespace) + 1 */ |
| 62 | char key[sizeof(log_namespace) + taglen]; |
| 63 | char *kp; |
| 64 | size_t i; |
| 65 | char c = 0; |
| 66 | /* |
| 67 | * Single layer cache of four properties. Priorities are: |
| 68 | * log.tag.<tag> |
| 69 | * persist.log.tag.<tag> |
| 70 | * log.tag |
| 71 | * persist.log.tag |
| 72 | * Where the missing tag matches all tags and becomes the |
| 73 | * system global default. We do not support ro.log.tag* . |
| 74 | */ |
| 75 | static char *last_tag; |
| 76 | static uint32_t global_serial; |
| 77 | uint32_t current_global_serial; |
| 78 | static struct cache tag_cache[2] = { |
| 79 | { NULL, -1, 0 }, |
| 80 | { NULL, -1, 0 } |
| 81 | }; |
| 82 | static struct cache global_cache[2] = { |
| 83 | { NULL, -1, 0 }, |
| 84 | { NULL, -1, 0 } |
| 85 | }; |
| 86 | |
| 87 | strcpy(key, log_namespace); |
| 88 | |
| 89 | pthread_mutex_lock(&lock); |
| 90 | |
| 91 | current_global_serial = __system_property_area_serial(); |
| 92 | |
| 93 | if (taglen) { |
| 94 | uint32_t current_local_serial = current_global_serial; |
| 95 | |
Mark Salyzyn | 16e1c9d | 2015-10-02 10:00:38 -0700 | [diff] [blame] | 96 | if (!last_tag || (last_tag[0] != tag[0]) || strcmp(last_tag + 1, tag + 1)) { |
Mark Salyzyn | c158456 | 2015-03-12 15:46:29 -0700 | [diff] [blame] | 97 | /* invalidate log.tag.<tag> cache */ |
| 98 | for(i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) { |
| 99 | tag_cache[i].pinfo = NULL; |
| 100 | tag_cache[i].serial = -1; |
| 101 | tag_cache[i].c = '\0'; |
| 102 | } |
| 103 | free(last_tag); |
| 104 | last_tag = NULL; |
| 105 | current_global_serial = -1; |
| 106 | } |
| 107 | if (!last_tag) { |
| 108 | last_tag = strdup(tag); |
| 109 | } |
Mark Salyzyn | 9568705 | 2014-10-02 11:12:28 -0700 | [diff] [blame] | 110 | strcpy(key + sizeof(log_namespace) - 1, tag); |
| 111 | |
Mark Salyzyn | c158456 | 2015-03-12 15:46:29 -0700 | [diff] [blame] | 112 | kp = key; |
| 113 | for(i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) { |
| 114 | if (current_local_serial != global_serial) { |
| 115 | refresh_cache(&tag_cache[i], kp); |
| 116 | } |
| 117 | |
| 118 | if (tag_cache[i].c) { |
| 119 | c = tag_cache[i].c; |
| 120 | break; |
| 121 | } |
| 122 | |
| 123 | kp = key + base_offset; |
Mark Salyzyn | 1f028b2 | 2014-10-08 15:58:40 -0700 | [diff] [blame] | 124 | } |
Mark Salyzyn | 9568705 | 2014-10-02 11:12:28 -0700 | [diff] [blame] | 125 | } |
Mark Salyzyn | c158456 | 2015-03-12 15:46:29 -0700 | [diff] [blame] | 126 | |
| 127 | switch (toupper(c)) { /* if invalid, resort to global */ |
| 128 | case 'V': |
| 129 | case 'D': |
| 130 | case 'I': |
| 131 | case 'W': |
| 132 | case 'E': |
| 133 | case 'F': /* Not officially supported */ |
| 134 | case 'A': |
| 135 | case 'S': |
| 136 | break; |
| 137 | default: |
| 138 | /* clear '.' after log.tag */ |
| 139 | key[sizeof(log_namespace) - 2] = '\0'; |
| 140 | |
| 141 | kp = key; |
| 142 | for(i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) { |
| 143 | if (current_global_serial != global_serial) { |
| 144 | refresh_cache(&global_cache[i], kp); |
| 145 | } |
| 146 | |
| 147 | if (global_cache[i].c) { |
| 148 | c = global_cache[i].c; |
| 149 | break; |
| 150 | } |
| 151 | |
| 152 | kp = key + base_offset; |
| 153 | } |
| 154 | break; |
| 155 | } |
| 156 | |
| 157 | global_serial = current_global_serial; |
| 158 | |
| 159 | pthread_mutex_unlock(&lock); |
| 160 | |
| 161 | switch (toupper(c)) { |
| 162 | case 'V': return ANDROID_LOG_VERBOSE; |
| 163 | case 'D': return ANDROID_LOG_DEBUG; |
| 164 | case 'I': return ANDROID_LOG_INFO; |
| 165 | case 'W': return ANDROID_LOG_WARN; |
| 166 | case 'E': return ANDROID_LOG_ERROR; |
| 167 | case 'F': /* FALLTHRU */ /* Not officially supported */ |
| 168 | case 'A': return ANDROID_LOG_FATAL; |
| 169 | case 'S': return -1; /* ANDROID_LOG_SUPPRESS */ |
Mark Salyzyn | 9568705 | 2014-10-02 11:12:28 -0700 | [diff] [blame] | 170 | } |
| 171 | return def; |
| 172 | } |
| 173 | |
| 174 | int __android_log_is_loggable(int prio, const char *tag, int def) |
| 175 | { |
Mark Salyzyn | 1f028b2 | 2014-10-08 15:58:40 -0700 | [diff] [blame] | 176 | int logLevel = __android_log_level(tag, def); |
Mark Salyzyn | 9568705 | 2014-10-02 11:12:28 -0700 | [diff] [blame] | 177 | return logLevel >= 0 && prio >= logLevel; |
| 178 | } |