diff options
| -rw-r--r-- | services/common_time/clock_recovery.cpp | 12 | ||||
| -rw-r--r-- | services/common_time/clock_recovery.h | 4 | ||||
| -rw-r--r-- | services/common_time/common_time_server.cpp | 190 | ||||
| -rw-r--r-- | services/common_time/common_time_server.h | 5 | ||||
| -rw-r--r-- | services/common_time/common_time_server_api.cpp | 3 | ||||
| -rw-r--r-- | services/common_time/utils.cpp | 115 | ||||
| -rw-r--r-- | services/common_time/utils.h | 35 |
7 files changed, 318 insertions, 46 deletions
diff --git a/services/common_time/clock_recovery.cpp b/services/common_time/clock_recovery.cpp index 6e914e590630..37cc15d8e0ac 100644 --- a/services/common_time/clock_recovery.cpp +++ b/services/common_time/clock_recovery.cpp @@ -225,6 +225,9 @@ bool ClockRecoveryLoop::pushDisciplineEvent(int64_t local_time, if (current_point == min_rtt || rtt < control_thresh_) { delta_f = delta = nominal_common_time - observed_common; + last_error_est_valid_ = true; + last_error_est_usec_ = delta; + // Compute the error then clamp to the panic threshold. If we ever // exceed this amt of error, its time to panic and reset the system. // Given that the error in the measurement of the error could be as @@ -258,7 +261,6 @@ bool ClockRecoveryLoop::pushDisciplineEvent(int64_t local_time, // Save error terms for later. last_delta_f_ = delta_f; - last_delta_ = delta; // Clamp CO to +/- 100ppm. if (CO < COmin) @@ -295,8 +297,8 @@ bool ClockRecoveryLoop::pushDisciplineEvent(int64_t local_time, int32_t ClockRecoveryLoop::getLastErrorEstimate() { Mutex::Autolock lock(&lock_); - if (last_delta_valid_) - return last_delta_; + if (last_error_est_valid_) + return last_error_est_usec_; else return ICommonClock::kErrorEstimateUnknown; } @@ -310,8 +312,8 @@ void ClockRecoveryLoop::reset_l(bool position, bool frequency) { } if (frequency) { - last_delta_valid_ = false; - last_delta_ = 0; + last_error_est_valid_ = false; + last_error_est_usec_ = 0; last_delta_f_ = 0.0; CO = 0.0f; lastCObias = CObias = 0.0f; diff --git a/services/common_time/clock_recovery.h b/services/common_time/clock_recovery.h index 20fbf96602c3..b6c87ffaf37e 100644 --- a/services/common_time/clock_recovery.h +++ b/services/common_time/clock_recovery.h @@ -108,8 +108,8 @@ class ClockRecoveryLoop { // parameters maintained while running and reset during a reset // of the frequency correction. - bool last_delta_valid_; - int32_t last_delta_; + bool last_error_est_valid_; + int32_t last_error_est_usec_; float last_delta_f_; int32_t integrated_error_; int32_t tgt_correction_; diff --git a/services/common_time/common_time_server.cpp b/services/common_time/common_time_server.cpp index 75df85e9f449..8982f05800c9 100644 --- a/services/common_time/common_time_server.cpp +++ b/services/common_time/common_time_server.cpp @@ -107,6 +107,9 @@ CommonTimeServer::CommonTimeServer() , mTimelineID(ICommonClock::kInvalidTimelineID) , mClockSynced(false) , mCommonClockHasClients(false) + , mStateChangeLog("Recent State Change Events", 30) + , mElectionLog("Recent Master Election Traffic", 30) + , mBadPktLog("Recent Bad Packet RX Info", 8) , mInitial_WhoIsMasterRequestTimeouts(0) , mClient_MasterDeviceID(0) , mClient_MasterDevicePriority(0) @@ -330,10 +333,11 @@ bool CommonTimeServer::runStateMachine_l() { // we are in any other state (CLIENT, RONIN or WAIT_FOR_ELECTION), // then transition to either INITIAL or MASTER depending on whether // or not our timeline is valid. - LOGI("Entering networkless mode interface is %s, " - "shouldAutoDisable = %s", - mBindIfaceValid ? "valid" : "invalid", - shouldAutoDisable() ? "true" : "false"); + mStateChangeLog.log(ANDROID_LOG_INFO, LOG_TAG, + "Entering networkless mode interface is %s, " + "shouldAutoDisable = %s", + mBindIfaceValid ? "valid" : "invalid", + shouldAutoDisable() ? "true" : "false"); if ((mState != ICommonClock::STATE_INITIAL) && (mState != ICommonClock::STATE_MASTER)) { if (mTimelineID == ICommonClock::kInvalidTimelineID) @@ -415,20 +419,23 @@ bool CommonTimeServer::setupSocket_l() { sockaddrToString(mMasterElectionEP, true, masterElectionEPStr, sizeof(masterElectionEPStr)); - LOGI("Building socket :: bind = %s master election = %s", - mBindIface.string(), masterElectionEPStr); + mStateChangeLog.log(ANDROID_LOG_INFO, LOG_TAG, + "Building socket :: bind = %s master election = %s", + mBindIface.string(), masterElectionEPStr); // TODO: add proper support for IPv6. Right now, we block IPv6 addresses at // the configuration interface level. if (AF_INET != mMasterElectionEP.ss_family) { - LOGW("TODO: add proper IPv6 support"); + mStateChangeLog.log(ANDROID_LOG_WARN, LOG_TAG, + "TODO: add proper IPv6 support"); goto bailout; } // open a UDP socket for the timeline serivce mSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (mSocket < 0) { - LOGE("Failed to create socket (errno = %d)", errno); + mStateChangeLog.log(ANDROID_LOG_ERROR, LOG_TAG, + "Failed to create socket (errno = %d)", errno); goto bailout; } @@ -440,8 +447,9 @@ bool CommonTimeServer::setupSocket_l() { rc = setsockopt(mSocket, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)); if (rc) { - LOGE("Failed to bind socket at to interface %s (errno = %d)", - ifr.ifr_name, errno); + mStateChangeLog.log(ANDROID_LOG_ERROR, LOG_TAG, + "Failed to bind socket at to interface %s " + "(errno = %d)", ifr.ifr_name, errno); goto bailout; } @@ -459,8 +467,9 @@ bool CommonTimeServer::setupSocket_l() { reinterpret_cast<const sockaddr *>(&bindAddr), sizeof(bindAddr)); if (rc) { - LOGE("Failed to bind socket to port %hu (errno = %d)", - ntohs(bindAddr.sin_port), errno); + mStateChangeLog.log(ANDROID_LOG_ERROR, LOG_TAG, + "Failed to bind socket to port %hu (errno = %d)", + ntohs(bindAddr.sin_port), errno); goto bailout; } @@ -483,17 +492,20 @@ bool CommonTimeServer::setupSocket_l() { rc = setsockopt(mSocket, IPPROTO_IP, IP_MULTICAST_LOOP, &zero, sizeof(zero)); if (rc == -1) { - LOGE("Failed to disable multicast loopback (errno = %d)", errno); + mStateChangeLog.log(ANDROID_LOG_ERROR, LOG_TAG, + "Failed to disable multicast loopback " + "(errno = %d)", errno); goto bailout; } } else if (ntohl(ipv4_addr->sin_addr.s_addr) == 0xFFFFFFFF) { // If the master election address is the broadcast address, then enable // the broadcast socket option - const int one = 1; rc = setsockopt(mSocket, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)); if (rc == -1) { - LOGE("Failed to enable broadcast (errno = %d)", errno); + mStateChangeLog.log(ANDROID_LOG_ERROR, LOG_TAG, + "Failed to enable broadcast (errno = %d)", + errno); goto bailout; } } else { @@ -507,7 +519,8 @@ bool CommonTimeServer::setupSocket_l() { // the local subnet) rc = setsockopt(mSocket, IPPROTO_IP, IP_TTL, &one, sizeof(one)); if (rc == -1) { - LOGE("Failed to set TTL to %d (errno = %d)", one, errno); + mStateChangeLog.log(ANDROID_LOG_ERROR, LOG_TAG, + "Failed to set TTL to %d (errno = %d)", one, errno); goto bailout; } @@ -569,6 +582,31 @@ bool CommonTimeServer::arbitrateMaster( ((devicePrio1 == devicePrio2) && (deviceID1 > deviceID2))); } +static void hexDumpToString(const uint8_t* src, size_t src_len, + char* dst, size_t dst_len) { + size_t offset; + size_t i; + + for (i = 0; (i < src_len) && (offset < dst_len); ++i) { + int res; + if (0 == (i % 16)) { + res = snprintf(dst + offset, dst_len - offset, "\n%04x :", i); + if (res < 0) + break; + offset += res; + if (offset >= dst_len) + break; + } + + res = snprintf(dst + offset, dst_len - offset, " %02x", src[i]); + if (res < 0) + break; + offset += res; + } + + dst[dst_len - 1] = 0; +} + bool CommonTimeServer::handlePacket() { uint8_t buf[256]; struct sockaddr_storage srcAddr; @@ -579,14 +617,24 @@ bool CommonTimeServer::handlePacket() { reinterpret_cast<const sockaddr *>(&srcAddr), &srcAddrLen); if (recvBytes < 0) { - LOGE("%s:%d recvfrom failed", __PRETTY_FUNCTION__, __LINE__); + mBadPktLog.log(ANDROID_LOG_ERROR, LOG_TAG, + "recvfrom failed (res %d, errno %d)", + recvBytes, errno); return false; } UniversalTimeServicePacket pkt; - recvBytes = pkt.deserializePacket(buf, recvBytes, mSyncGroupID); - if (recvBytes < 0) + if (pkt.deserializePacket(buf, recvBytes, mSyncGroupID) < 0) { + char hex[256]; + char srcEPStr[64]; + + hexDumpToString(buf, static_cast<size_t>(recvBytes), hex, sizeof(hex)); + sockaddrToString(srcAddr, true, srcEPStr, sizeof(srcEPStr)); + + mBadPktLog.log("Failed to parse %d byte packet from %s.%s", + recvBytes, srcEPStr, hex); return false; + } bool result; switch (pkt.packetType) { @@ -614,8 +662,13 @@ bool CommonTimeServer::handlePacket() { break; default: { - LOGD("%s:%d unknown packet type(%d)", - __PRETTY_FUNCTION__, __LINE__, pkt.packetType); + char srcEPStr[64]; + sockaddrToString(srcAddr, true, srcEPStr, sizeof(srcEPStr)); + + mBadPktLog.log(ANDROID_LOG_WARN, LOG_TAG, + "unknown packet type (%d) from %s", + pkt.packetType, srcEPStr); + result = false; } break; } @@ -699,6 +752,14 @@ bool CommonTimeServer::handleTimeoutWaitForElection() { bool CommonTimeServer::handleWhoIsMasterRequest( const WhoIsMasterRequestPacket* request, const sockaddr_storage& srcAddr) { + + char srcEPStr[64]; + sockaddrToString(srcAddr, true, srcEPStr, sizeof(srcEPStr)); + mElectionLog.log("RXed WhoIs master request while in state %s. " + "src %s reqTID %016llx ourTID %016llx", + stateToString(mState), srcEPStr, + request->timelineID, mTimelineID); + if (mState == ICommonClock::STATE_MASTER) { // is this request related to this master's timeline? if (request->timelineID != ICommonClock::kInvalidTimelineID && @@ -710,6 +771,13 @@ bool CommonTimeServer::handleWhoIsMasterRequest( pkt.deviceID = mDeviceID; pkt.devicePriority = effectivePriority(); + mElectionLog.log("TXing WhoIs master resp to %s while in state %s. " + "ourTID %016llx ourGID %016llx ourDID %016llx " + "ourPrio %u", + srcEPStr, stateToString(mState), + mTimelineID, mSyncGroupID, + pkt.deviceID, pkt.devicePriority); + uint8_t buf[256]; ssize_t bufSz = pkt.serializePacket(buf, sizeof(buf)); if (bufSz < 0) @@ -761,6 +829,17 @@ bool CommonTimeServer::handleWhoIsMasterRequest( bool CommonTimeServer::handleWhoIsMasterResponse( const WhoIsMasterResponsePacket* response, const sockaddr_storage& srcAddr) { + char srcEPStr[64]; + sockaddrToString(srcAddr, true, srcEPStr, sizeof(srcEPStr)); + mElectionLog.log("RXed WhoIs master response while in state %s. " + "src %s respTID %016llx respDID %016llx respPrio %u " + "ourTID %016llx", + stateToString(mState), srcEPStr, + response->timelineID, + response->deviceID, + static_cast<uint32_t>(response->devicePriority), + mTimelineID); + if (mState == ICommonClock::STATE_INITIAL || mState == ICommonClock::STATE_RONIN) { return becomeClient(srcAddr, response->deviceID, @@ -916,6 +995,14 @@ bool CommonTimeServer::handleMasterAnnouncement( uint8_t newDevicePrio = packet->devicePriority; uint64_t newTimelineID = packet->timelineID; + char srcEPStr[64]; + sockaddrToString(srcAddr, true, srcEPStr, sizeof(srcEPStr)); + mElectionLog.log("RXed master announcement while in state %s. " + "src %s srcDevID %lld srcPrio %u srcTID %016llx", + stateToString(mState), srcEPStr, + newDeviceID, static_cast<uint32_t>(newDevicePrio), + newTimelineID); + if (mState == ICommonClock::STATE_INITIAL || mState == ICommonClock::STATE_RONIN || mState == ICommonClock::STATE_WAIT_FOR_ELECTION) { @@ -972,6 +1059,15 @@ bool CommonTimeServer::sendWhoIsMasterRequest() { uint8_t buf[256]; ssize_t bufSz = pkt.serializePacket(buf, sizeof(buf)); if (bufSz >= 0) { + char dstEPStr[64]; + sockaddrToString(mMasterElectionEP, true, dstEPStr, sizeof(dstEPStr)); + mElectionLog.log("TXing WhoIs master request to %s while in state %s. " + "ourTID %016llx ourGID %016llx ourDID %016llx " + "ourPrio %u", + dstEPStr, stateToString(mState), + mTimelineID, mSyncGroupID, + pkt.senderDeviceID, pkt.senderDevicePriority); + ssize_t sendBytes = sendto( mSocket, buf, bufSz, 0, reinterpret_cast<const sockaddr *>(&mMasterElectionEP), @@ -1048,6 +1144,15 @@ bool CommonTimeServer::sendMasterAnnouncement() { uint8_t buf[256]; ssize_t bufSz = pkt.serializePacket(buf, sizeof(buf)); if (bufSz >= 0) { + char dstEPStr[64]; + sockaddrToString(mMasterElectionEP, true, dstEPStr, sizeof(dstEPStr)); + mElectionLog.log("TXing Master announcement to %s while in state %s. " + "ourTID %016llx ourGID %016llx ourDID %016llx " + "ourPrio %u", + dstEPStr, stateToString(mState), + mTimelineID, mSyncGroupID, + pkt.deviceID, pkt.devicePriority); + ssize_t sendBytes = sendto( mSocket, buf, bufSz, 0, reinterpret_cast<const sockaddr *>(&mMasterElectionEP), @@ -1070,15 +1175,16 @@ bool CommonTimeServer::becomeClient(const sockaddr_storage& masterEP, sockaddrToString(masterEP, true, newEPStr, sizeof(newEPStr)); sockaddrToString(mMasterEP, mMasterEPValid, oldEPStr, sizeof(oldEPStr)); - LOGI("%s --> CLIENT (%s) :%s" - " OldMaster: %02x-%014llx::%016llx::%s" - " NewMaster: %02x-%014llx::%016llx::%s", - stateToString(mState), cause, - (mTimelineID != timelineID) ? " (new timeline)" : "", - mClient_MasterDevicePriority, mClient_MasterDeviceID, - mTimelineID, oldEPStr, - masterDevicePriority, masterDeviceID, - timelineID, newEPStr); + mStateChangeLog.log(ANDROID_LOG_INFO, LOG_TAG, + "%s --> CLIENT (%s) :%s" + " OldMaster: %02x-%014llx::%016llx::%s" + " NewMaster: %02x-%014llx::%016llx::%s", + stateToString(mState), cause, + (mTimelineID != timelineID) ? " (new timeline)" : "", + mClient_MasterDevicePriority, mClient_MasterDeviceID, + mTimelineID, oldEPStr, + masterDevicePriority, masterDeviceID, + timelineID, newEPStr); if (mTimelineID != timelineID) { // start following a new timeline @@ -1131,11 +1237,12 @@ bool CommonTimeServer::becomeMaster(const char* cause) { notifyClockSync(); } - LOGI("%s --> MASTER (%s) : %s timeline %016llx", - stateToString(mState), cause, - (oldTimelineID == mTimelineID) ? "taking ownership of" - : "creating new", - mTimelineID); + mStateChangeLog.log(ANDROID_LOG_INFO, LOG_TAG, + "%s --> MASTER (%s) : %s timeline %016llx", + stateToString(mState), cause, + (oldTimelineID == mTimelineID) ? "taking ownership of" + : "creating new", + mTimelineID); memset(&mMasterEP, 0, sizeof(mMasterEP)); mMasterEPValid = false; @@ -1164,7 +1271,8 @@ bool CommonTimeServer::becomeRonin(const char* cause) { mMasterEPValid = false; if (mCommonClock.isValid()) { - LOGI("%s --> RONIN (%s) : lost track of previously valid timeline " + mStateChangeLog.log(ANDROID_LOG_INFO, LOG_TAG, + "%s --> RONIN (%s) : lost track of previously valid timeline " "%02x-%014llx::%016llx::%s (%d TXed %d RXed %d RXExpired)", stateToString(mState), cause, mClient_MasterDevicePriority, mClient_MasterDeviceID, @@ -1177,7 +1285,8 @@ bool CommonTimeServer::becomeRonin(const char* cause) { setState(ICommonClock::STATE_RONIN); return sendWhoIsMasterRequest(); } else { - LOGI("%s --> INITIAL (%s) : never synced timeline " + mStateChangeLog.log(ANDROID_LOG_INFO, LOG_TAG, + "%s --> INITIAL (%s) : never synced timeline " "%02x-%014llx::%016llx::%s (%d TXed %d RXed %d RXExpired)", stateToString(mState), cause, mClient_MasterDevicePriority, mClient_MasterDeviceID, @@ -1191,7 +1300,8 @@ bool CommonTimeServer::becomeRonin(const char* cause) { } bool CommonTimeServer::becomeWaitForElection(const char* cause) { - LOGI("%s --> WAIT_FOR_ELECTION (%s) : dropping out of election," + mStateChangeLog.log(ANDROID_LOG_INFO, LOG_TAG, + "%s --> WAIT_FOR_ELECTION (%s) : dropping out of election," " waiting %d mSec for completion.", stateToString(mState), cause, kWaitForElection_TimeoutMs); @@ -1201,7 +1311,9 @@ bool CommonTimeServer::becomeWaitForElection(const char* cause) { } bool CommonTimeServer::becomeInitial(const char* cause) { - LOGI("Entering INITIAL (%s), total reset.", cause); + mStateChangeLog.log(ANDROID_LOG_INFO, LOG_TAG, + "Entering INITIAL (%s), total reset.", + cause); setState(ICommonClock::STATE_INITIAL); diff --git a/services/common_time/common_time_server.h b/services/common_time/common_time_server.h index 2401b77a34c9..a745144400e4 100644 --- a/services/common_time/common_time_server.h +++ b/services/common_time/common_time_server.h @@ -238,6 +238,11 @@ class CommonTimeServer : public Thread { // interface AND currently active common clock clients. bool mCommonClockHasClients; + // Internal logs used for dumpsys. + LogRing mStateChangeLog; + LogRing mElectionLog; + LogRing mBadPktLog; + // Configuration info struct sockaddr_storage mMasterElectionEP; // Endpoint over which we conduct master election String8 mBindIface; // Endpoint for the service to bind to. diff --git a/services/common_time/common_time_server_api.cpp b/services/common_time/common_time_server_api.cpp index 657a382cd5f7..f37dc854aab7 100644 --- a/services/common_time/common_time_server_api.cpp +++ b/services/common_time/common_time_server_api.cpp @@ -354,6 +354,9 @@ status_t CommonTimeServer::dumpClockInterface(int fd, dump_printf("Active Clients : %u\n", activeClients); mClient_PacketRTTLog.dumpLog(fd, mCommonClock); + mStateChangeLog.dumpLog(fd); + mElectionLog.dumpLog(fd); + mBadPktLog.dumpLog(fd); } return NO_ERROR; diff --git a/services/common_time/utils.cpp b/services/common_time/utils.cpp index 3ed259944cf1..ad13772dc86c 100644 --- a/services/common_time/utils.cpp +++ b/services/common_time/utils.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +#define LOG_TAG "common_time" +#include <utils/Log.h> + #include "utils.h" namespace android { @@ -46,4 +49,116 @@ int Timeout::msecTillTimeout(nsecs_t nowTime) { return static_cast<int>(delta); } +LogRing::LogRing(const char* header, size_t entries) + : mSize(entries) + , mWr(0) + , mIsFull(false) + , mHeader(header) { + mRingBuffer = new Entry[mSize]; + if (NULL == mRingBuffer) + LOGE("Failed to allocate log ring with %u entries.", mSize); +} + +LogRing::~LogRing() { + if (NULL != mRingBuffer) + delete[] mRingBuffer; +} + +void LogRing::log(int prio, const char* tag, const char* fmt, ...) { + va_list argp; + va_start(argp, fmt); + internalLog(prio, tag, fmt, argp); + va_end(argp); +} + +void LogRing::log(const char* fmt, ...) { + va_list argp; + va_start(argp, fmt); + internalLog(0, NULL, fmt, argp); + va_end(argp); +} + +void LogRing::internalLog(int prio, + const char* tag, + const char* fmt, + va_list argp) { + if (NULL != mRingBuffer) { + Mutex::Autolock lock(&mLock); + String8 s(String8::formatV(fmt, argp)); + Entry* last = NULL; + + if (mIsFull || mWr) + last = &(mRingBuffer[(mWr + mSize - 1) % mSize]); + + + if ((NULL != last) && !last->s.compare(s)) { + gettimeofday(&(last->last_ts), NULL); + ++last->count; + } else { + gettimeofday(&mRingBuffer[mWr].first_ts, NULL); + mRingBuffer[mWr].last_ts = mRingBuffer[mWr].first_ts; + mRingBuffer[mWr].count = 1; + mRingBuffer[mWr].s.setTo(s); + + mWr = (mWr + 1) % mSize; + if (!mWr) + mIsFull = true; + } + } + + if (NULL != tag) + LOG_PRI_VA(prio, tag, fmt, argp); +} + +void LogRing::dumpLog(int fd) { + if (NULL == mRingBuffer) + return; + + Mutex::Autolock lock(&mLock); + + if (!mWr && !mIsFull) + return; + + char buf[1024]; + int res; + size_t start = mIsFull ? mWr : 0; + size_t count = mIsFull ? mSize : mWr; + static const char* kTimeFmt = "%a %b %d %Y %H:%M:%S"; + + res = snprintf(buf, sizeof(buf), "\n%s\n", mHeader); + if (res > 0) + write(fd, buf, res); + + for (size_t i = 0; i < count; ++i) { + struct tm t; + char timebuf[64]; + char repbuf[96]; + size_t ndx = (start + i) % mSize; + + if (1 != mRingBuffer[ndx].count) { + localtime_r(&mRingBuffer[ndx].last_ts.tv_sec, &t); + strftime(timebuf, sizeof(timebuf), kTimeFmt, &t); + snprintf(repbuf, sizeof(repbuf), + " (repeated %d times, last was %s.%03ld)", + mRingBuffer[ndx].count, + timebuf, + mRingBuffer[ndx].last_ts.tv_usec / 1000); + repbuf[sizeof(repbuf) - 1] = 0; + } else { + repbuf[0] = 0; + } + + localtime_r(&mRingBuffer[ndx].first_ts.tv_sec, &t); + strftime(timebuf, sizeof(timebuf), kTimeFmt, &t); + res = snprintf(buf, sizeof(buf), "[%2d] %s.%03ld :: %s%s\n", + i, timebuf, + mRingBuffer[ndx].first_ts.tv_usec / 1000, + mRingBuffer[ndx].s.string(), + repbuf); + + if (res > 0) + write(fd, buf, res); + } +} + } // namespace android diff --git a/services/common_time/utils.h b/services/common_time/utils.h index d3545c972b07..c28cf0ac33d7 100644 --- a/services/common_time/utils.h +++ b/services/common_time/utils.h @@ -20,6 +20,8 @@ #include <stdint.h> #include <unistd.h> +#include <utils/String8.h> +#include <utils/threads.h> #include <utils/Timers.h> namespace android { @@ -43,6 +45,39 @@ class Timeout { nsecs_t mSystemEndTime; }; +class LogRing { + public: + LogRing(const char* header, size_t entries); + ~LogRing(); + + // Send a log message to logcat as well as storing it in the ring buffer. + void log(int prio, const char* tag, const char* fmt, ...); + + // Add a log message the ring buffer, do not send the message to logcat. + void log(const char* fmt, ...); + + // Dump the log to an fd (dumpsys style) + void dumpLog(int fd); + + private: + class Entry { + public: + uint32_t count; + struct timeval first_ts; + struct timeval last_ts; + String8 s; + }; + + Mutex mLock; + Entry* mRingBuffer; + size_t mSize; + size_t mWr; + bool mIsFull; + const char* mHeader; + + void internalLog(int prio, const char* tag, const char* fmt, va_list va); +}; + } // namespace android #endif // __UTILS_H__ |