liblog: Instrument logging of logd write drops

- If logger system is prostrated, send an event message with the
  liblog tag from the associated UID and PID with a count of
  dropped messages once logging is resumed.
- Added to the README a description of the error return values.
- Describe in the README the appropriate mitigations for dropped
  messages.
- If the caller sees this message, then
  /proc/sys/net/unix/max_dgram_qlen is likely too small

Change-Id: Iaf387b9e5e1b6aa93bebc7481f9e8353732e3229
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index 0208c73..c8a6162 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -13,12 +13,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include <endian.h>
 #include <errno.h>
 #include <fcntl.h>
 #if !defined(_WIN32)
 #include <pthread.h>
 #endif
 #include <stdarg.h>
+#include <stdatomic.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -172,6 +174,7 @@
     size_t i, payload_size;
     static uid_t last_uid = AID_ROOT; /* logd *always* starts up as AID_ROOT */
     static pid_t last_pid = (pid_t) -1;
+    static atomic_int_fast32_t dropped;
 
     if (last_uid == AID_ROOT) { /* have we called to get the UID yet? */
         last_uid = getuid();
@@ -206,7 +209,6 @@
     pmsg_header.uid = last_uid;
     pmsg_header.pid = last_pid;
 
-    header.id = log_id;
     header.tid = gettid();
     header.realtime.tv_sec = ts.tv_sec;
     header.realtime.tv_nsec = ts.tv_nsec;
@@ -216,6 +218,28 @@
     newVec[1].iov_base   = (unsigned char *) &header;
     newVec[1].iov_len    = sizeof(header);
 
+    if (logd_fd > 0) {
+        int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
+        if (snapshot) {
+            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[2].iov_base = &buffer;
+            newVec[2].iov_len  = sizeof(buffer);
+
+            ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, 2));
+            if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
+                atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
+            }
+        }
+    }
+
+    header.id = log_id;
+
     for (payload_size = 0, i = header_length; i < nr + header_length; i++) {
         newVec[i].iov_base = vec[i - header_length].iov_base;
         payload_size += newVec[i].iov_len = vec[i - header_length].iov_len;
@@ -281,6 +305,8 @@
 
     if (ret > (ssize_t)sizeof(header)) {
         ret -= sizeof(header);
+    } else if (ret == -EAGAIN) {
+        atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
     }
 #endif