The Android log system involves three modules: server LogD, client, and LogCAT.

The client side, the Android system provides is the dynamic link library liblog.so, in the up through JNI encapsulation of the Java API, the provision of procedures (upper procedures) call.

Logcat tool for outputting system logs to terminals or redirecting them to files.

buffer ID

/ * *@hide* / public static final int LOG_ID_MAIN = 0;
/ * *@hide* / public static final int LOG_ID_RADIO = 1;
/ * *@hide* / public static final int LOG_ID_EVENTS = 2;
/ * *@hide* / public static final int LOG_ID_SYSTEM = 3;
/ * *@hide* / public static final int LOG_ID_CRASH = 4;
Copy the code

priority

/** * Priority constant for the println method; use Log.v. */
public static final int VERBOSE = 2;

/** * Priority constant for the println method; use Log.d. */
public static final int DEBUG = 3;

/** * Priority constant for the println method; use Log.i. */
public static final int INFO = 4;

/** * Priority constant for the println method; use Log.w. */
public static final int WARN = 5;

/** * Priority constant for the println method; use Log.e. */
public static final int ERROR = 6;

/** * Priority constant for the println method. */
public static final int ASSERT = 7;
Copy the code

frameworks/base/core/java/android/util/Slog.java

public static int i(String tag, String msg) {
    return Log.println_native(Log.LOG_ID_SYSTEM, Log.INFO, tag, msg);
}

public static int i(String tag, String msg, Throwable tr) {
    return Log.println_native(Log.LOG_ID_SYSTEM, Log.INFO, tag,
            msg + '\n' + Log.getStackTraceString(tr));
}
Copy the code

frameworks/base/core/java/android/util/Log.java

/ * *@hide* / public static native int println_native(int bufID,
        int priority, String tag, String msg);
Copy the code

The frameworks. Jar is the SDK for Android. Let’s go to JNI.

JNI

frameworks/base/core/jni/android_util_Log.cpp

static const JNINativeMethod gMethods[] = {
    /* name, signature, funcPtr */
    { "isLoggable"."(Ljava/lang/String; I)Z", (void*) android_util_Log_isLoggable },
    { "println_native"."(IILjava/lang/String; Ljava/lang/String;) I", (void*) android_util_Log_println_native },
    { "logger_entry_max_payload_native"."()I", (void*) android_util_Log_logger_entry_max_payload_native },
};

/* * In class android.util.Log: * public static native int println_native(int buffer, int priority, String tag, String msg) */
static jint android_util_Log_println_native(JNIEnv* env, jobject clazz, jint bufID, jint priority, jstring tagObj, jstring msgObj)
{
    const char* tag = NULL;
    const char* msg = NULL;

    if (msgObj == NULL) {
        jniThrowNullPointerException(env, "println needs a message");
        return - 1;
    }

    if (bufID < 0 || bufID >= LOG_ID_MAX) {
        jniThrowNullPointerException(env, "bad bufID");
        return - 1;
    }

    if(tagObj ! =NULL)
        tag = env->GetStringUTFChars(tagObj, NULL);
    msg = env->GetStringUTFChars(msgObj, NULL);
    // After some basic checking, call the following function, which is called in
    int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);

    if(tag ! =NULL)
        env->ReleaseStringUTFChars(tagObj, tag);
    env->ReleaseStringUTFChars(msgObj, msg);

    return res;
}
Copy the code

The JNI part is compiled to generate the dynamic link library libandroid_runtime.so. The library is loaded when the virtual machine starts and is called by programs in the virtual machine.

liblog.so

Liblog library code path system/core/liblog/.

system/core/liblog/logger_write.c

LIBLOG_ABI_PUBLIC int __android_log_buf_write(int bufID, int prio,
                                              const char* tag, const char* msg) {
  struct iovec vec[3].
  char tmp_tag[32];

  if(! tag) tag ="";

  / *XXX:This needs to go! * /
  if(bufID ! = LOG_ID_RADIO) {switch (tag[0]) {
      case 'H':
        if (strcmp(tag + 1."HTC_RIL" + 1)) break;
        goto inform;
      case 'R':
        /* Any log tag with "RIL" as the prefix */
        if (strncmp(tag + 1."RIL" + 1.strlen("RIL") - 1)) break;
        goto inform;
      case 'Q':
        /* Any log tag with "QC_RIL" as the prefix */
        if (strncmp(tag + 1."QC_RIL" + 1.strlen("QC_RIL") - 1)) break;
        goto inform;
      case 'I':
        /* Any log tag with "IMS" as the prefix */
        if (strncmp(tag + 1."IMS" + 1.strlen("IMS") - 1)) break;
        goto inform;
      case 'A':
        if (strcmp(tag + 1."AT" + 1)) break;
        goto inform;
      case 'G':
        if (strcmp(tag + 1."GSM" + 1)) break;
        goto inform;
      case 'S':
        if (strcmp(tag + 1."STK" + 1) && strcmp(tag + 1."SMS" + 1)) break;
        goto inform;
      case 'C':
        if (strcmp(tag + 1."CDMA" + 1)) break;
        goto inform;
      case 'P':
        if (strcmp(tag + 1."PHONE" + 1)) break;
      /* FALLTHRU */
      inform:
        bufID = LOG_ID_RADIO;
        snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
        tag = tmp_tag;
      /* FALLTHRU */
      default:
        break; }}#if __BIONIC__
  if (prio == ANDROID_LOG_FATAL) {
    android_set_abort_message(msg);
  }
#endif

  vec[0].iov_base = (unsigned char*)&prio;
  vec[0].iov_len = 1;
  vec[1].iov_base = (void*)tag;
  vec[1].iov_len = strlen(tag) + 1;
  vec[2].iov_base = (void*)msg;
  vec[2].iov_len = strlen(msg) + 1;

  return write_to_log(bufID, vec, 3);
}

static int (*write_to_log)(log_id_t, struct iovec* vec,
                           size_t nr) = __write_to_log_init;

static int __write_to_log_init(log_id_t log_id, struct iovec* vec, size_t nr) {
  int ret, save_errno = errno;

  __android_log_lock();

  if (write_to_log == __write_to_log_init) {
    ret = __write_to_log_initialize();
    if (ret < 0) {
      __android_log_unlock();
      if(! list_empty(&__android_log_persist_write)) { __write_to_log_daemon(log_id, vec, nr); } errno = save_errno;return ret;
    }

    write_to_log = __write_to_log_daemon;
  }

  __android_log_unlock();

  ret = write_to_log(log_id, vec, nr);
  errno = save_errno;
  return ret;
}
Copy the code
static int __write_to_log_daemon(log_id_t log_id, struct iovec* vec, size_t nr) {
  struct android_log_transport_write* node;
  int ret, save_errno;
  struct timespec ts;
  size_t len, i;

  for (len = i = 0; i < nr; ++i) {
    len += vec[i].iov_len;
  }
  if(! len) {return -EINVAL;
  }

  save_errno = errno;
#if defined(__ANDROID__)
  clock_gettime(android_log_clockid(), &ts);

  if (log_id == LOG_ID_SECURITY) {
    if (vec[0].iov_len < 4) {
      errno = save_errno;
      return -EINVAL;
    }

    ret = check_log_uid_permissions();
    if (ret < 0) {
      errno = save_errno;
      return ret;
    }
    if(! __android_log_security()) {/* If only we could reset downstream logd counter */
      errno = save_errno;
      return-EPERM; }}else if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
    const char* tag;
    size_t len;
    EventTagMap *m, *f;

    if (vec[0].iov_len < 4) {
      errno = save_errno;
      return -EINVAL;
    }

    tag = NULL;
    len = 0;
    f = NULL;
    m = (EventTagMap*)atomic_load(&tagMap);

    if(! m) { ret = __android_log_trylock(); m = (EventTagMap*)atomic_load(&tagMap);/* trylock flush cache */
      if(! m) { m = android_openEventTagMap(NULL);
        if (ret) { /* trylock failed, use local copy, mark for close */
          f = m;
        } else {
          if(! m) {/* One chance to open map file */
            m = (EventTagMap*)(uintptr_t)-1LL;
          }
          atomic_store(&tagMap, (uintptr_t)m); }}if(! ret) {/* trylock succeeded, unlock */__android_log_unlock(); }}if(m && (m ! = (EventTagMap*)(uintptr_t)-1LL)) {
      tag = android_lookupEventTag_len(m, &len, get4LE(vec[0].iov_base));
    }
    ret = __android_log_is_loggable_len(ANDROID_LOG_INFO, tag, len,
                                        ANDROID_LOG_VERBOSE);
    if (f) { /* local copy marked for close */
      android_closeEventTagMap(f);
    }
    if(! ret) { errno = save_errno;return-EPERM; }}else {
    /* Validate the incoming tag, tag content can not split across iovec */
    char prio = ANDROID_LOG_VERBOSE;
    const char* tag = vec[0].iov_base;
    size_t len = vec[0].iov_len;
    if(! tag) { len =0;
    }
    if (len > 0) {
      prio = *tag;
      if (len > 1) {
        --len;
        ++tag;
      } else {
        len = vec[1].iov_len;
        tag = ((const char*)vec[1].iov_base);
        if(! tag) { len =0; }}}/* tag must be nul terminated */
    if (tag && strnlen(tag, len) >= len) {
      tag = NULL;
    }

    if(! __android_log_is_loggable_len(prio, tag, len -1, ANDROID_LOG_VERBOSE)) {
      errno = save_errno;
      return-EPERM; }}#else
  /* simulate clock_gettime(CLOCK_REALTIME, &ts); * /
  {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    ts.tv_sec = tv.tv_sec;
    ts.tv_nsec = tv.tv_usec * 1000;
  }
#endif

  ret = 0;
  i = 1 << log_id;
  write_transport_for_each(node, &__android_log_transport_write) {
    if (node->logMask & i) {
      ssize_t retval;
      retval = (*node->write)(log_id, &ts, vec, nr);
      if (ret >= 0) {
        ret = retval;
      }
    }
  }

  write_transport_for_each(node, &__android_log_persist_write) {
    if (node->logMask & i) {
      (void)(*node->write)(log_id, &ts, vec, nr);
    }
  }

  errno = save_errno;
  return ret;
}
Copy the code
static int logdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec,
                     size_t nr) {
  ssize_t ret;
  int sock;
  static const unsigned headerLength = 1;
  struct iovec newVec[nr + headerLength];
  android_log_header_t header;
  size_t i, payloadSize;
  static atomic_int_fast32_t dropped;
  static atomic_int_fast32_t droppedSecurity;

  sock = atomic_load(&logdLoggerWrite.context.sock);
  if (sock < 0) switch (sock) {
      case -ENOTCONN:
      case -ECONNREFUSED:
      case -ENOENT:
        break;
      default:
        return -EBADF;
    }

  /* logd, after initialization and priv drop */
  if (__android_log_uid() == AID_LOGD) {
    /* * ignore log messages we send to ourself (logd). * Such log messages are often generated by libraries we depend on * which use standard Android logging. */
    return 0;
  }

  /* * struct { * // what we provide to socket * android_log_header_t header; * // caller provides * union { * struct { * char prio; * char payload[]; * } string; * struct { * uint32_t tag * char payload[]; * } binary; *}; *}; * /

  header.tid = gettid();
  header.realtime.tv_sec = ts->tv_sec;
  header.realtime.tv_nsec = ts->tv_nsec;

  newVec[0].iov_base = (unsigned char*)&header;
  newVec[0].iov_len = sizeof(header);

  if (sock >= 0) {
    int32_t snapshot =
        atomic_exchange_explicit(&droppedSecurity, 0, memory_order_relaxed);
    if (snapshot) {
      android_log_event_int_t buffer;

      header.id = LOG_ID_SECURITY;
      buffer.header.tag = htole32(LIBLOG_LOG_TAG);
      buffer.payload.type = EVENT_TYPE_INT;
      buffer.payload.data = htole32(snapshot);

      newVec[headerLength].iov_base = &buffer;
      newVec[headerLength].iov_len = sizeof(buffer);

      ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2));
      if(ret ! = (ssize_t) (sizeof(header) + sizeof(buffer))) {
        atomic_fetch_add_explicit(&droppedSecurity, snapshot,
                                  memory_order_relaxed);
      }
    }
    snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
    if (snapshot &&
        __android_log_is_loggable_len(ANDROID_LOG_INFO, "liblog".strlen("liblog"), ANDROID_LOG_VERBOSE)) {
      android_log_event_int_t buffer;

      header.id = LOG_ID_EVENTS;
      buffer.header.tag = htole32(LIBLOG_LOG_TAG);
      buffer.payload.type = EVENT_TYPE_INT;
      buffer.payload.data = htole32(snapshot);

      newVec[headerLength].iov_base = &buffer;
      newVec[headerLength].iov_len = sizeof(buffer);

      ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2));
      if(ret ! = (ssize_t) (sizeof(header) + sizeof(buffer))) {
        atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
      }
    }
  }

  header.id = logId;

  for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
    newVec[i].iov_base = vec[i - headerLength].iov_base;
    payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;

    if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {
      newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;
      if (newVec[i].iov_len) {
        ++i;
      }
      break; }}/* * The write below could be lost, but will never block. * * ENOTCONN occurs if logd has died. * ENOENT occurs if logd is not running and socket is missing. * ECONNREFUSED occurs if we can not reconnect to logd. * EAGAIN occurs if logd is overloaded. */
  if (sock < 0) {
    ret = sock;
  } else {
    ret = TEMP_FAILURE_RETRY(writev(sock, newVec, i));
    if (ret < 0) { ret = -errno; }}switch (ret) {
    case -ENOTCONN:
    case -ECONNREFUSED:
    case -ENOENT:
      if (__android_log_trylock()) {
        return ret; /* in a signal handler? try again when less stressed */
      }
      __logdClose(ret);
      ret = logdOpen();
      __android_log_unlock();

      if (ret < 0) {
        return ret;
      }

      ret = TEMP_FAILURE_RETRY(
          writev(atomic_load(&logdLoggerWrite.context.sock), newVec, i));
      if (ret < 0) {
        ret = -errno;
      }
    /* FALLTHRU */
    default:
      break;
  }

  if (ret > (ssize_t)sizeof(header)) {
    ret -= sizeof(header);
  } else if (ret == -EAGAIN) {
    atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
    if (logId == LOG_ID_SECURITY) {
      atomic_fetch_add_explicit(&droppedSecurity, 1, memory_order_relaxed); }}return ret;
}
Copy the code
static int __write_to_log_initialize() {
  struct android_log_transport_write* transport;
  struct listnode* n;
  int i = 0, ret = 0;
  // Initialize the variables __android_log_transport_write and __android_log_persist_write
  __android_log_config_write();
  write_transport_for_each_safe(transport, n, &__android_log_transport_write) {
    __android_log_cache_available(transport);
    if(! transport->logMask) { list_remove(&transport->node);continue;
    }
    if(! transport->open || ((*transport->open)() <0)) {
      if (transport->close) {
        (*transport->close)();
      }
      list_remove(&transport->node);
      continue;
    }
    ++ret;
  }
  write_transport_for_each_safe(transport, n, &__android_log_persist_write) {
    __android_log_cache_available(transport);
    if(! transport->logMask) { list_remove(&transport->node);continue;
    }
    if(! transport->open || ((*transport->open)() <0)) {
      if (transport->close) {
        (*transport->close)();
      }
      list_remove(&transport->node);
      continue;
    }
    ++i;
  }
  if(! ret && ! i) {return -ENODEV;
  }

  return ret;
}
Copy the code

system\system\core\liblog\config_write.c

LIBLOG_HIDDEN void __android_log_config_write() {
  // __android_log_transport the default value is LOGGER_DEFAULT
  if (__android_log_transport & LOGGER_LOCAL) {
    extern struct android_log_transport_write localLoggerWrite;

    __android_log_add_transport(&__android_log_transport_write,
                                &localLoggerWrite);
  }

  if ((__android_log_transport == LOGGER_DEFAULT) ||
      (__android_log_transport & LOGGER_LOGD)) {
#if (FAKE_LOG_DEVICE == 0)
    extern struct android_log_transport_write logdLoggerWrite;
    extern struct android_log_transport_write pmsgLoggerWrite;

    __android_log_add_transport(&__android_log_transport_write,
                                &logdLoggerWrite);
    __android_log_add_transport(&__android_log_persist_write, &pmsgLoggerWrite);
#else
    extern struct android_log_transport_write fakeLoggerWrite;

    __android_log_add_transport(&__android_log_transport_write,
                                &fakeLoggerWrite);
#endif
  }

  if (__android_log_transport & LOGGER_STDERR) {
    extern struct android_log_transport_write stderrLoggerWrite;

    /* * stderr logger should be primary if we can be the only one, or if * already in the primary list. Otherwise land in the persist list. * Remember we can be called here if we are already initialized. */
    if (list_empty(&__android_log_transport_write)) {
      __android_log_add_transport(&__android_log_transport_write,
                                  &stderrLoggerWrite);
    } else {
      struct android_log_transport_write* transp;
      write_transport_for_each(transp, &__android_log_transport_write) {
        if (transp == &stderrLoggerWrite) {
          return; } } __android_log_add_transport(&__android_log_persist_write, &stderrLoggerWrite); }}}Copy the code

Establish a socket connection with the server

system\core\liblog\logd_writer.c

static int logdOpen(a) {
  int i, ret = 0;

  i = atomic_load(&logdLoggerWrite.context.sock);
  if (i < 0) {
    int sock = TEMP_FAILURE_RETRY(
        socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
    if (sock < 0) {
      ret = -errno;
    } else {
      struct sockaddr_un un;
      memset(&un, 0.sizeof(struct sockaddr_un));
      un.sun_family = AF_UNIX;
      strcpy(un.sun_path, "/dev/socket/logdw");
      // A socket connection is established between logDW and logD to send client log data to logD
      if (TEMP_FAILURE_RETRY(connect(sock, (struct sockaddr*)&un,
                                     sizeof(struct sockaddr_un))) < 0) {
        ret = -errno;
        switch (ret) {
          case -ENOTCONN:
          case -ECONNREFUSED:
          case -ENOENT:
            i = atomic_exchange(&logdLoggerWrite.context.sock, ret);
          /* FALLTHRU */
          default:
            break;
        }
        close(sock);
      } else {
        ret = atomic_exchange(&logdLoggerWrite.context.sock, sock);
        if ((ret >= 0) && (ret ! = sock)) { close(ret); } ret =0; }}}return ret;
}
Copy the code

The variables logdLoggerWrite and pmsgLoggerWrite

system\system\core\liblog\logd_writer.c

LIBLOG_HIDDEN struct android_log_transport_write logdLoggerWrite = {
  .node = { &logdLoggerWrite.node, &logdLoggerWrite.node },
  .context.sock = -EBADF,
  .name = "logd",
  .available = logdAvailable,
  .open = logdOpen,
  .close = logdClose,
  .write = logdWrite,
};
Copy the code

system\system\core\liblog\pmsg_writer.c

LIBLOG_HIDDEN struct android_log_transport_write pmsgLoggerWrite = {
  .node = { &pmsgLoggerWrite.node, &pmsgLoggerWrite.node },
  .context.fd = - 1,
  .name = "pmsg",
  .available = pmsgAvailable,
  .open = pmsgOpen,
  .close = pmsgClose,
  .write = pmsgWrite,
};
Copy the code

log daemon

The log daemon is started by the init process that parses rc files. The logd source is located in system/core/logd/

system/core/logd/logd.rc

service logd /system/bin/logd
    socket logd stream 0666 logd logd
    socket logdr seqpacket 0666 logd logd
    socket logdw dgram+passcred 0222 logd logd
    file /proc/kmsg r
    file /dev/kmsg w
    user logd
    group logd system package_info readproc
    writepid /dev/cpuset/system-background/tasks
Copy the code

system/core/logd/Android.mk

LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE:= logd LOCAL_INIT_RC := logd.rc LOCAL_SRC_FILES := \ main.cpp \ ... . LOCAL_SHARED_LIBRARIES := \ libsysutils \ liblog \ libcutils \ libbase \ libpackagelistparser event_flag := -DAUDITD_LOG_TAG=1003 -DLOGD_LOG_TAG=1004 LOCAL_CFLAGS := -Werror $(event_flag) include $(BUILD_EXECUTABLE) include $(call first-makefiles-under,$(LOCAL_PATH))Copy the code

Main process

system\system\core\logd\main.cpp

int main(int argc, char* argv[]) {
    // Time zone Settings. Set the time to UTC
    setenv("TZ"."UTC".1);
    // When logd is executed, if the argument --reinit takes the if logic
    if ((argc > 1) && argv[1] &&!strcmp(argv[1]."--reinit")) {
        return issueReinit();
    }

    static const char dev_kmsg[] = "/dev/kmsg";
    // Get the kernel log file descriptor from the environment variable. If the return value is less than 0, the file /dev/kmsg is opened directly for writing
    fdDmesg = android_get_control_file(dev_kmsg);
    if (fdDmesg < 0) {
        fdDmesg = TEMP_FAILURE_RETRY(open(dev_kmsg, O_WRONLY | O_CLOEXEC));
    }

    int fdPmesg = - 1;
    // Whether to enable the kernel log file. If yes, try to obtain the kernel log file descriptor for reading
    bool klogd = __android_logger_property_get_bool(
        "ro.logd.kernel",
        BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
    if (klogd) {
        static const char proc_kmsg[] = "/proc/kmsg";
        fdPmesg = android_get_control_file(proc_kmsg);
        if (fdPmesg < 0) {
            fdPmesg = TEMP_FAILURE_RETRY(
                open(proc_kmsg, O_RDONLY | O_NDELAY | O_CLOEXEC));
        }
        if (fdPmesg < 0) android::prdebug("Failed to open %s\n", proc_kmsg);
    }

    // Create a semaphore shared between the threads of the three current processes
    sem_init(&reinit, 0.0);
    sem_init(&uidName, 0.0);
    sem_init(&sem_name, 0.1);
    pthread_attr_t attr;
    if(! pthread_attr_init(&attr)) {struct sched_param param;

        memset(&param, 0.sizeof(param));
        pthread_attr_setschedparam(&attr, &param);
        pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
        if(! pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {pthread_t thread;
            reinit_running = true;
            // Create a thread. After the thread is successfully created, the corresponding thread function is run, in this case reinit_thread_start
            if (pthread_create(&thread, &attr, reinit_thread_start, nullptr)) {
                reinit_running = false;
            }
        }
        pthread_attr_destroy(&attr);
    }

    bool auditd =
        __android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
    if(drop_privs(klogd, auditd) ! =0) {
        return - 1;
    }

    // Serves the purpose of managing the last logs times read on a
    // socket connection, and as a reader lock on a range of log
    // entries.

    LastLogTimes* times = new LastLogTimes();

    // LogBuffer is the object which is responsible for holding all
    // log entries.

    logBuf = new LogBuffer(times);

    signal(SIGHUP, reinit_signal_handler);

    if (__android_logger_property_get_bool(
            "logd.statistics". BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST | BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE)) { logBuf->enableStatistics(); }// LogReader listens on /dev/socket/logdr. When a client
    // connects, log entries in the LogBuffer are written to the client.

    LogReader* reader = new LogReader(logBuf);
    if (reader->startListener()) {
        exit(1);
    }

    // LogListener listens on /dev/socket/logdw for client
    // initiated log messages. New log entries are added to LogBuffer
    // and LogReader is notified to send updates to connected clients.

    LogListener* swl = new LogListener(logBuf, reader);
    // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
    if (swl->startListener(600)) {
        exit(1);
    }

    // Command listener listens on /dev/socket/logd for incoming logd
    // administrative commands.

    CommandListener* cl = new CommandListener(logBuf, reader, swl);
    if (cl->startListener()) {
        exit(1);
    }

    // LogAudit listens on NETLINK_AUDIT socket for selinux
    // initiated log messages. New log entries are added to LogBuffer
    // and LogReader is notified to send updates to connected clients.

    LogAudit* al = nullptr;
    if (auditd) {
        al = new LogAudit(logBuf, reader,
                          __android_logger_property_get_bool(
                              "ro.logd.auditd.dmesg", BOOL_DEFAULT_TRUE)
                              ? fdDmesg
                              : - 1);
    }

    LogKlog* kl = nullptr;
    if (klogd) {
        kl = newLogKlog(logBuf, reader, fdDmesg, fdPmesg, al ! =nullptr);
    }

    readDmesg(al, kl);

    // failure is an option ... messages are in dmesg (required by standard)

    if (kl && kl->startListener()) {
        delete kl;
    }

    if (al && al->startListener()) {
        delete al;
    }

    TEMP_FAILURE_RETRY(pause());

    exit(0);
}
Copy the code
static void readDmesg(LogAudit* al, LogKlog* kl) {
    if(! al && ! kl) {return;
    }

    int rc = klogctl(KLOG_SIZE_BUFFER, nullptr.0);
    if (rc <= 0) {
        return;
    }

    // Margin for additional input race or trailing nul
    ssize_t len = rc + 1024;
    std: :unique_ptr<char[]> buf(new char[len]);

    // Get logs from kernel space to buF
    rc = klogctl(KLOG_READ_ALL, buf.get(), len);
    if (rc <= 0) {
        return;
    }

    if (rc < len) {
        len = rc + 1;
    }
    buf[--len] = '\ 0';

    if (kl && kl->isMonotonic()) {
        kl->synchronize(buf.get(), len);
    }

    ssize_t sublen;
    for (char *ptr = nullptr, *tok = buf.get();
         (rc >= 0) &&!!!!! (tok = android::log_strntok_r(tok, len, ptr, sublen)); tok =nullptr) {
        if ((sublen <= 0) | |! *tok)continue;
        if (al) {
            rc = al->log(tok, sublen);
        }
        if (kl) {
            rc = kl->log(tok, sublen); }}}Copy the code

logcat

system/core/logcat/logcat_main.cpp

int main(int argc, char** argv, char** envp) {
    // Create logcat context
    android_logcat_context ctx = create_android_logcat();
    if(! ctx)return - 1;
    signal(SIGPIPE, exit);
    // Run logcat
    int retval = android_logcat_run_command(ctx, - 1.- 1, argc, argv, envp);
    int ret = android_logcat_destroy(&ctx);
    if(! ret) ret = retval;return ret;
}
Copy the code

The main program is this section and relies on liblogcat.so

liblogcat.so

system/core/logcat/logcat.cpp

int android_logcat_run_command(android_logcat_context ctx,
                               int output, int error,
                               int argc, char* const* argv,
                               char* const* envp) {
    android_logcat_context_internal* context = ctx;

    context->output_fd = output;
    context->error_fd = error;
    context->argc = argc;
    context->argv = argv;
    context->envp = envp;
    context->stop = false;
    context->thread_stopped = false;
    return __logcat(context);
}

static int__logcat(android_logcat_context_internal* context) { ... .// Standard output and standard error
    context->output = stdout;
    context->error = stderr;

    // Log output to file
    if (filename) {  // We supplied an output file redirected in command line
        context->output = fopen(filename, "web");
    }
    
    // Default output_fd=-1, in which case output to standard output (screen)
    if ((context->output_fd < 0) && context->output) {
        context->output_fd = fileno(context->output);
    }
    // If it is' logcat --help ', the help information is printed
    if (argc == 2&&!strcmp(argv[1]."--help")) {
        show_help(context);
        context->retval = EXIT_SUCCESS;
        goto exit;
    }

    while(! context->stop && (! context->maxCount || (context->printCount < context->maxCount))) {struct log_msg log_msg;
        int ret = android_logger_list_read(logger_list, &log_msg);
        if(! ret) { logcat_panic(context, HELP_FALSE,"read: unexpected EOF! \n");
            break;
        }

        if (ret < 0) {
            if (ret == -EAGAIN) break;

            if (ret == -EIO) {
                logcat_panic(context, HELP_FALSE, "read: unexpected EOF! \n");
                break;
            }
            if (ret == -EINVAL) {
                logcat_panic(context, HELP_FALSE, "read: unexpected length.\n");
                break;
            }
            logcat_panic(context, HELP_FALSE, "logcat read failure\n");
            break;
        }

        log_device_t* d;
        for (d = context->devices; d; d = d->next) {
            if (android_name_to_log_id(d->device) == log_msg.id()) break;
        }
        if(! d) { context->devCount =2; // set to Multiple
            d = &unexpected;
            d->binary = log_msg.id() == LOG_ID_EVENTS;
        }

        if(dev ! = d) { dev = d; maybePrintStart(context, dev, printDividers);if (context->stop) break;
        }
        // In normal cases, no parameter B is added
        if (context->printBinary) {
            printBinary(context, &log_msg);
        } else {
            processBuffer(context, dev, &log_msg);
        }
    }

close:
    // Short and sweet. Implemented generic version in android_logcat_destroy.
    while(!!!!! (dev = context->devices)) { context->devices = dev->next;delete dev;
    }
    android_logger_list_free(logger_list);

exit:
    // close write end of pipe to help things along
    if (context->output_fd == context->fds[1]) {
        android::close_output(context);
    }
    if (context->error_fd == context->fds[1]) {
        android::close_error(context);
    }
    if (context->fds[1] > =0) {
        // NB: should be closed by the above
        int save_errno = errno;
        close(context->fds[1]);
        errno = save_errno;
        context->fds[1] = - 1;
    }
    context->thread_stopped = true;
    return context->retval;
}

static void processBuffer(android_logcat_context_internal* context,
                          log_device_t* dev, struct log_msg* buf) {
    int bytesWritten = 0;
    int err;
    AndroidLogEntry entry;
    char binaryMsgBuf[1024];

    if (dev->binary) {
        if(! context->eventTagMap && ! context->hasOpenedEventTagMap) { context->eventTagMap = android_openEventTagMap(nullptr);
            context->hasOpenedEventTagMap = true;
        }
        err = android_log_processBinaryLogBuffer(
            &buf->entry_v1, &entry, context->eventTagMap, binaryMsgBuf,
            sizeof(binaryMsgBuf));
        // printf(">>> pri=%d len=%d msg='%s'\n",
        // entry.priority, entry.messageLen, entry.message);
    } else {
        err = android_log_processLogBuffer(&buf->entry_v1, &entry);
    }
    if ((err < 0) && !context->debug) return;

    if (android_log_shouldPrintLine(
            context->logformat, std: :string(entry.tag, entry.tagLen).c_str(),
            entry.priority)) {
        bool match = regexOk(context, entry);

        context->printCount += match;
        if (match || context->printItAnyways) {
            bytesWritten = android_log_printLogLine(context->logformat,
                                                    context->output_fd, &entry);

            if (bytesWritten < 0) {
                logcat_panic(context, HELP_FALSE, "output error");
                return;
            }
        }
    }

    context->outByteCount += bytesWritten;

    if (context->logRotateSizeKBytes > 0 &&
        (context->outByteCount / 1024) >= context->logRotateSizeKBytes) { rotateLogs(context); }}LIBLOG_ABI_PUBLIC int android_log_printLogLine(AndroidLogFormat* p_format,
                                               int fd,
                                               const AndroidLogEntry* entry) {
  int ret;
  char defaultBuffer[512];
  char* outBuffer = NULL;
  size_t totalLen;

  outBuffer = android_log_formatLogLine(
      p_format, defaultBuffer, sizeof(defaultBuffer), entry, &totalLen);

  if(! outBuffer)return - 1;

  do {
    // Output logs, fd or standard output or file
    ret = write(fd, outBuffer, totalLen);
  } while (ret < 0 && errno == EINTR);

  if (ret < 0) {
    fprintf(stderr."+++ LOG: write failed (errno=%d)\n", errno);
    ret = 0;
    goto done;
  }

  if (((size_t)ret) < totalLen) {
    fprintf(stderr."+++ LOG: write partial (%d of %d)\n", ret, (int)totalLen);
    goto done;
  }

done:
  if(outBuffer ! = defaultBuffer) {free(outBuffer);
  }

  return ret;
}
Copy the code

The structure of the body

system/core/liblog/include/log/logprint.h

typedef struct AndroidLogEntry_t {
  time_t tv_sec;
  long tv_nsec;
  android_LogPriority priority;
  int32_t uid;
  int32_t pid;
  int32_t tid;
  const char* tag;
  size_t tagLen;
  size_t messageLen;
  const char* message;
} AndroidLogEntry;
Copy the code

A log contains time, log priority, UID, PID, TID, label, and information.

debug

APP log

In a class Log/I/v/d w/e/WTF frameworks/base/core/Java/android/util/Log. Java. The output LOG_ID is MAIN, which means the log is stored in the MAIN wake up buffer.

import android.util.Log;

Log.v("TAG"."msg");
Copy the code

The framework of log

Framework of logging calls Slog class v/d/I/w/e/WTF/println frameworks/base/core/Java/android/util/Slog. Java. The output LOG_ID is SYSTEM, which means the log is stored in the SYSTEM wake up buffer

import android.util.Slog;

Slog.v("TAG"."msg");
Copy the code

Native log

system/core/liblog/include/log/log_main.h

This header file defines the macro, ALOGV ALOGD/ALOGI ALOGW/ALOGE/ALOG_ASSERT calls for native code. If you use ALOGV, you need to use #define LOG_NDEBUG 0 at the beginning of the source file. By including the header file #include

.

system/core/libutils/include/utils/Log.h

#include <log/log.h>
Copy the code

system/core/liblog/include/log/log.h

#include <log/log_main.h>
Copy the code

In fact, these are eventually called to liblog.so inside the method __android_log_buf_write.

#define LOG_NDEBUG 0
#include <utils/Log.h>
ALOGV("ddddddddddddddddyy");
ALOGV("xxxxxx%d, buffersize:%d\n", timeoutMillis, bufferSize);
Copy the code

The kernel log

printk

Log buffer

system/core/logd/LogBuffer.cpp

unsigned long mMaxSize[LOG_ID_MAX];
#define log_buffer_size(id) mMaxSize[id]
#define LOG_BUFFER_MIN_SIZE (64 * 1024UL)
#define LOG_BUFFER_MAX_SIZE (256 * 1024 * 1024UL)
Copy the code

The mMaxSize array holds the buffer size for each ring buffer. Buffer has a reasonable range of values ranging from 64KB to 256MB. There are a total of 8 ring buffers. Id corresponds to the following structure, where 0 is MAIN, 3 is SYSTEM, and 4 is CRASH.

typedef enum log_id {
  LOG_ID_MIN = 0,

  LOG_ID_MAIN = 0,
  LOG_ID_RADIO = 1,
  LOG_ID_EVENTS = 2,
  LOG_ID_SYSTEM = 3,
  LOG_ID_CRASH = 4,
  LOG_ID_STATS = 5,
  LOG_ID_SECURITY = 6,
  LOG_ID_KERNEL = 7./* place last, third-parties can not use it */

  LOG_ID_MAX
} log_id_t;
Copy the code

When the log daemon starts, it allocates the size of the ring buffer according to preset logic. You can also change the size of the ring buffer by using logcat -g XX.

View the size of the log buffer of the device

# logcat -g                                                           
main: ring buffer is 64Kb (60Kb consumed), max entry is 5120b, max payload is 4068b
system: ring buffer is 64Kb (63Kb consumed), max entry is 5120b, max payload is 4068b
crash: ring buffer is 64Kb (0b consumed), max entry is 5120b, max payload is 4068b
Copy the code

Set the size of the device log buffer

# logcat -G 100K/20MB
Copy the code

View the log content of a buffer

# logcat -b main/system
Copy the code

The logcat command can output the contents of all buffers.

Knowledge summary

stdout

Stdout is a macro defined in the stdio.h header file

bionic/libc/include/stdio.h

#define stdout (&__sF[1])
Copy the code

bionic/libc/stdio/stdio.cpp

FILE __sF[3] = {
  std(__SRD, STDIN_FILENO),
  std(__SWR, STDOUT_FILENO),
  std(__SWR|__SNBF, STDERR_FILENO),
};

Copy the code

bionic/libc/include/unistd.h

#define STDIN_FILENO    0
#define STDOUT_FILENO   1
#define STDERR_FILENO   2
Copy the code

It’s actually /proc/self.f/1