|  |  |  | #include "selfdrive/loggerd/logger.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <assert.h>
 | 
					
						
							|  |  |  | #include <errno.h>
 | 
					
						
							|  |  |  | #include <stdint.h>
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include <sys/stat.h>
 | 
					
						
							|  |  |  | #include <time.h>
 | 
					
						
							|  |  |  | #include <unistd.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <fstream>
 | 
					
						
							|  |  |  | #include <iostream>
 | 
					
						
							|  |  |  | #include <streambuf>
 | 
					
						
							|  |  |  | #ifdef QCOM
 | 
					
						
							|  |  |  | #include <cutils/properties.h>
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "cereal/messaging/messaging.h"
 | 
					
						
							|  |  |  | #include "selfdrive/common/params.h"
 | 
					
						
							|  |  |  | #include "selfdrive/common/swaglog.h"
 | 
					
						
							|  |  |  | #include "selfdrive/common/version.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ***** logging helpers *****
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void append_property(const char* key, const char* value, void *cookie) {
 | 
					
						
							|  |  |  |   std::vector<std::pair<std::string, std::string> > *properties =
 | 
					
						
							|  |  |  |     (std::vector<std::pair<std::string, std::string> > *)cookie;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   properties->push_back(std::make_pair(std::string(key), std::string(value)));
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int logger_mkpath(char* file_path) {
 | 
					
						
							|  |  |  |   assert(file_path && *file_path);
 | 
					
						
							|  |  |  |   char* p;
 | 
					
						
							|  |  |  |   for (p=strchr(file_path+1, '/'); p; p=strchr(p+1, '/')) {
 | 
					
						
							|  |  |  |     *p = '\0';
 | 
					
						
							|  |  |  |     if (mkdir(file_path, 0777)==-1) {
 | 
					
						
							|  |  |  |       if (errno != EEXIST) {
 | 
					
						
							|  |  |  |         *p = '/';
 | 
					
						
							|  |  |  |         return -1;
 | 
					
						
							|  |  |  |       }
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  |     *p = '/';
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  |   return 0;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ***** log metadata *****
 | 
					
						
							|  |  |  | kj::Array<capnp::word> logger_build_init_data() {
 | 
					
						
							|  |  |  |   MessageBuilder msg;
 | 
					
						
							|  |  |  |   auto init = msg.initEvent().initInitData();
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (Hardware::EON()) {
 | 
					
						
							|  |  |  |     init.setDeviceType(cereal::InitData::DeviceType::NEO);
 | 
					
						
							|  |  |  |   } else if (Hardware::TICI()) {
 | 
					
						
							|  |  |  |     init.setDeviceType(cereal::InitData::DeviceType::TICI);
 | 
					
						
							|  |  |  |   } else {
 | 
					
						
							|  |  |  |     init.setDeviceType(cereal::InitData::DeviceType::PC);
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   init.setVersion(capnp::Text::Reader(COMMA_VERSION));
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   std::ifstream cmdline_stream("/proc/cmdline");
 | 
					
						
							|  |  |  |   std::vector<std::string> kernel_args;
 | 
					
						
							|  |  |  |   std::string buf;
 | 
					
						
							|  |  |  |   while (cmdline_stream >> buf) {
 | 
					
						
							|  |  |  |     kernel_args.push_back(buf);
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   auto lkernel_args = init.initKernelArgs(kernel_args.size());
 | 
					
						
							|  |  |  |   for (int i=0; i<kernel_args.size(); i++) {
 | 
					
						
							|  |  |  |     lkernel_args.set(i, kernel_args[i]);
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   init.setKernelVersion(util::read_file("/proc/version"));
 | 
					
						
							|  |  |  |   init.setOsVersion(util::read_file("/VERSION"));
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef QCOM
 | 
					
						
							|  |  |  |   {
 | 
					
						
							|  |  |  |     std::vector<std::pair<std::string, std::string> > properties;
 | 
					
						
							|  |  |  |     property_list(append_property, (void*)&properties);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto lentries = init.initAndroidProperties().initEntries(properties.size());
 | 
					
						
							|  |  |  |     for (int i=0; i<properties.size(); i++) {
 | 
					
						
							|  |  |  |       auto lentry = lentries[i];
 | 
					
						
							|  |  |  |       lentry.setKey(properties[i].first);
 | 
					
						
							|  |  |  |       lentry.setValue(properties[i].second);
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   init.setDirty(!getenv("CLEAN"));
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // log params
 | 
					
						
							|  |  |  |   Params params = Params();
 | 
					
						
							|  |  |  |   init.setGitCommit(params.get("GitCommit"));
 | 
					
						
							|  |  |  |   init.setGitBranch(params.get("GitBranch"));
 | 
					
						
							|  |  |  |   init.setGitRemote(params.get("GitRemote"));
 | 
					
						
							|  |  |  |   init.setPassive(params.getBool("Passive"));
 | 
					
						
							|  |  |  |   init.setDongleId(params.get("DongleId"));
 | 
					
						
							|  |  |  |   {
 | 
					
						
							|  |  |  |     std::map<std::string, std::string> params_map;
 | 
					
						
							|  |  |  |     params.readAll(¶ms_map);
 | 
					
						
							|  |  |  |     auto lparams = init.initParams().initEntries(params_map.size());
 | 
					
						
							|  |  |  |     int i = 0;
 | 
					
						
							|  |  |  |     for (auto& kv : params_map) {
 | 
					
						
							|  |  |  |       auto lentry = lparams[i];
 | 
					
						
							|  |  |  |       lentry.setKey(kv.first);
 | 
					
						
							|  |  |  |       lentry.setValue(capnp::Data::Reader((const kj::byte*)kv.second.data(), kv.second.size()));
 | 
					
						
							|  |  |  |       i++;
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  |   return capnp::messageToFlatArray(msg);
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::string logger_get_route_name() {
 | 
					
						
							|  |  |  |   char route_name[64] = {'\0'};
 | 
					
						
							|  |  |  |   time_t rawtime = time(NULL);
 | 
					
						
							|  |  |  |   struct tm timeinfo;
 | 
					
						
							|  |  |  |   localtime_r(&rawtime, &timeinfo);
 | 
					
						
							|  |  |  |   strftime(route_name, sizeof(route_name), "%Y-%m-%d--%H-%M-%S", &timeinfo);
 | 
					
						
							|  |  |  |   return route_name;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void log_init_data(LoggerState *s) {
 | 
					
						
							|  |  |  |   auto bytes = s->init_data.asBytes();
 | 
					
						
							|  |  |  |   logger_log(s, bytes.begin(), bytes.size(), s->has_qlog);
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void log_sentinel(LoggerState *s, cereal::Sentinel::SentinelType type, int signal=0) {
 | 
					
						
							|  |  |  |   MessageBuilder msg;
 | 
					
						
							|  |  |  |   auto sen = msg.initEvent().initSentinel();
 | 
					
						
							|  |  |  |   sen.setType(type);
 | 
					
						
							|  |  |  |   sen.setSignal(signal);
 | 
					
						
							|  |  |  |   auto bytes = msg.toBytes();
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   logger_log(s, bytes.begin(), bytes.size(), true);
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ***** logging functions *****
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void logger_init(LoggerState *s, const char* log_name, bool has_qlog) {
 | 
					
						
							|  |  |  |   umask(0);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   pthread_mutex_init(&s->lock, NULL);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   s->part = -1;
 | 
					
						
							|  |  |  |   s->has_qlog = has_qlog;
 | 
					
						
							|  |  |  |   s->route_name = logger_get_route_name();
 | 
					
						
							|  |  |  |   snprintf(s->log_name, sizeof(s->log_name), "%s", log_name);
 | 
					
						
							|  |  |  |   s->init_data = logger_build_init_data();
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static LoggerHandle* logger_open(LoggerState *s, const char* root_path) {
 | 
					
						
							|  |  |  |   int err;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   LoggerHandle *h = NULL;
 | 
					
						
							|  |  |  |   for (int i=0; i<LOGGER_MAX_HANDLES; i++) {
 | 
					
						
							|  |  |  |     if (s->handles[i].refcnt == 0) {
 | 
					
						
							|  |  |  |       h = &s->handles[i];
 | 
					
						
							|  |  |  |       break;
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  |   assert(h);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   snprintf(h->segment_path, sizeof(h->segment_path),
 | 
					
						
							|  |  |  |           "%s/%s--%d", root_path, s->route_name.c_str(), s->part);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   snprintf(h->log_path, sizeof(h->log_path), "%s/%s.bz2", h->segment_path, s->log_name);
 | 
					
						
							|  |  |  |   snprintf(h->qlog_path, sizeof(h->qlog_path), "%s/qlog.bz2", h->segment_path);
 | 
					
						
							|  |  |  |   snprintf(h->lock_path, sizeof(h->lock_path), "%s.lock", h->log_path);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   err = logger_mkpath(h->log_path);
 | 
					
						
							|  |  |  |   if (err) return NULL;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FILE* lock_file = fopen(h->lock_path, "wb");
 | 
					
						
							|  |  |  |   if (lock_file == NULL) return NULL;
 | 
					
						
							|  |  |  |   fclose(lock_file);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   h->log = std::make_unique<BZFile>(h->log_path);
 | 
					
						
							|  |  |  |   if (s->has_qlog) {
 | 
					
						
							|  |  |  |     h->q_log = std::make_unique<BZFile>(h->qlog_path);
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   pthread_mutex_init(&h->lock, NULL);
 | 
					
						
							|  |  |  |   h->refcnt++;
 | 
					
						
							|  |  |  |   return h;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int logger_next(LoggerState *s, const char* root_path,
 | 
					
						
							|  |  |  |                             char* out_segment_path, size_t out_segment_path_len,
 | 
					
						
							|  |  |  |                             int* out_part) {
 | 
					
						
							|  |  |  |   bool is_start_of_route = !s->cur_handle;
 | 
					
						
							|  |  |  |   if (!is_start_of_route) log_sentinel(s, cereal::Sentinel::SentinelType::END_OF_SEGMENT);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   pthread_mutex_lock(&s->lock);
 | 
					
						
							|  |  |  |   s->part++;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   LoggerHandle* next_h = logger_open(s, root_path);
 | 
					
						
							|  |  |  |   if (!next_h) {
 | 
					
						
							|  |  |  |     pthread_mutex_unlock(&s->lock);
 | 
					
						
							|  |  |  |     return -1;
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (s->cur_handle) {
 | 
					
						
							|  |  |  |     lh_close(s->cur_handle);
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  |   s->cur_handle = next_h;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (out_segment_path) {
 | 
					
						
							|  |  |  |     snprintf(out_segment_path, out_segment_path_len, "%s", next_h->segment_path);
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  |   if (out_part) {
 | 
					
						
							|  |  |  |     *out_part = s->part;
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   pthread_mutex_unlock(&s->lock);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // write beggining of log metadata
 | 
					
						
							|  |  |  |   log_init_data(s);
 | 
					
						
							|  |  |  |   log_sentinel(s, is_start_of_route ? cereal::Sentinel::SentinelType::START_OF_ROUTE : cereal::Sentinel::SentinelType::START_OF_SEGMENT);
 | 
					
						
							|  |  |  |   return 0;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | LoggerHandle* logger_get_handle(LoggerState *s) {
 | 
					
						
							|  |  |  |   pthread_mutex_lock(&s->lock);
 | 
					
						
							|  |  |  |   LoggerHandle* h = s->cur_handle;
 | 
					
						
							|  |  |  |   if (h) {
 | 
					
						
							|  |  |  |     pthread_mutex_lock(&h->lock);
 | 
					
						
							|  |  |  |     h->refcnt++;
 | 
					
						
							|  |  |  |     pthread_mutex_unlock(&h->lock);
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  |   pthread_mutex_unlock(&s->lock);
 | 
					
						
							|  |  |  |   return h;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void logger_log(LoggerState *s, uint8_t* data, size_t data_size, bool in_qlog) {
 | 
					
						
							|  |  |  |   pthread_mutex_lock(&s->lock);
 | 
					
						
							|  |  |  |   if (s->cur_handle) {
 | 
					
						
							|  |  |  |     lh_log(s->cur_handle, data, data_size, in_qlog);
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  |   pthread_mutex_unlock(&s->lock);
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void logger_close(LoggerState *s, ExitHandler *exit_handler) {
 | 
					
						
							|  |  |  |   int signal = exit_handler == nullptr ? 0 : exit_handler->signal.load();
 | 
					
						
							|  |  |  |   log_sentinel(s, cereal::Sentinel::SentinelType::END_OF_ROUTE, signal);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   pthread_mutex_lock(&s->lock);
 | 
					
						
							|  |  |  |   if (s->cur_handle) {
 | 
					
						
							|  |  |  |     lh_close(s->cur_handle);
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  |   pthread_mutex_unlock(&s->lock);
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void lh_log(LoggerHandle* h, uint8_t* data, size_t data_size, bool in_qlog) {
 | 
					
						
							|  |  |  |   pthread_mutex_lock(&h->lock);
 | 
					
						
							|  |  |  |   assert(h->refcnt > 0);
 | 
					
						
							|  |  |  |   h->log->write(data, data_size);
 | 
					
						
							|  |  |  |   if (in_qlog && h->q_log) {
 | 
					
						
							|  |  |  |     h->q_log->write(data, data_size);
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  |   pthread_mutex_unlock(&h->lock);
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void lh_close(LoggerHandle* h) {
 | 
					
						
							|  |  |  |   pthread_mutex_lock(&h->lock);
 | 
					
						
							|  |  |  |   assert(h->refcnt > 0);
 | 
					
						
							|  |  |  |   h->refcnt--;
 | 
					
						
							|  |  |  |   if (h->refcnt == 0) {
 | 
					
						
							|  |  |  |     h->log.reset(nullptr);
 | 
					
						
							|  |  |  |     h->q_log.reset(nullptr);
 | 
					
						
							|  |  |  |     unlink(h->lock_path);
 | 
					
						
							|  |  |  |     pthread_mutex_unlock(&h->lock);
 | 
					
						
							|  |  |  |     pthread_mutex_destroy(&h->lock);
 | 
					
						
							|  |  |  |     return;
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  |   pthread_mutex_unlock(&h->lock);
 | 
					
						
							|  |  |  | }
 |