2

I've been looking at libbinder.so, specifically IPCThreadState.cpp, line 781

In libbinder.so, writes are serialized using this line: TextOutput::Bundle _b(alog); which locks a mutex.

The "call tree" for the writes is:
alog << "Sending commands to driver: " << indent;

template<typename T>
TextOutput& operator<<(TextOutput& to, const T& val)
{
    std::stringstream strbuf;
    strbuf << val;
    std::string str = strbuf.str();
    to.print(str.c_str(), str.size());
    return to;
}

status_t BufferedTextOutput::print(const char* txt, size_t len)

virtual status_t writeLines(const struct iovec& vec, size_t N)
{
        //android_writevLog(&vec, N);       <-- this is now a no-op
        if (N != 1) ALOGI("WARNING: writeLines N=%zu\n", N);
        ALOGI("%.*s", (int)vec.iov_len, (const char*) vec.iov_base);
        return NO_ERROR;
}

#define ALOGI(x...) fprintf(stderr, "svcmgr: " x)

I understand how writes to log are serialized within libbinder.so but how are writes serialized between multiple .so libraries?

libbinder.so writes to stderr but surely there are other libs that also write to stderr.

Bob
  • 4,576
  • 7
  • 39
  • 107
  • My guess: if you have multiple threads and one is using `binder`, the other write to `stderr` directly, it's likely that they are not synced. So you may see interleaved characters in log. You can do such tests. – Mine Jul 18 '17 at 03:23
  • @Mine no they are synched. I've seen the code. I even reference it and link to it in my question. – Bob Jul 18 '17 at 03:24
  • The code in `libbinder.so` is synced, but other libraries using `stderr` may not. – Mine Jul 18 '17 at 05:11
  • @Mine you didn't get my question: if two libraries using stderr are synched internally, how are the writes synched across libraries? – Bob Jul 18 '17 at 12:47
  • What I mean is, the write is not synced across libraries. – Mine Jul 19 '17 at 02:33
  • @Mine not saying you're wrong but that seems unlikely. Logging would be near unusable. – Bob Jul 21 '17 at 21:13

2 Answers2

1

The lock is required to synchronize parts of the .<< chain. fprintf() is synchronized by the system.

Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
0

It's going to come down to how the underlying libc (Bionic on Android, not glibc as commonly found on Linux) implemented stdout and stderr.

On Linux glibc buffers stdout at the line level; that is, any thread that writes anything to stdout, followed by a \n, that line will be printed on the terminal intact, not interleaved. In contrast, stderr is not buffered - it's output immediately, which means that two threads writing to stderr simulatanouesly will cause the output to be interleaved.

On Android I think (I am not an Android programmer) it's different. From what I can tell stdout and stderr are directed through to something called logcat. Kinda makes sense - there's no terminal on which stdout and stderr is displayed, so why not have it hoovered up by some service? I'm speculating, but I strongly suspect that all Bionic does with stderr and stdout is write data down an IPC pipe, with logcat at the other end.

The thing about pipes in the Linux kernel is that writes to the pipe are atomic (for writes below 4kbytes). So, as long as the application's thread's output to stdout or stderr results in a single call to write() to that IPC pipe, it will be atomic and therefore not interleaved. So if a thread in the application calls something like fprintf(stderr, "%i %s %c\n", var, str, c) and Bionic builds a string which it then submits to the IPC pipe with a single write(logcatpipe, buf, len), then you're good.

I re-emphasise that this is mere speculation; it might help further matters. But if it is correct, then you won't get interleaving no matter how many threads simultaneously write to stderr or stdout.

EDIT

This S.O. Question might be useful. The solutions there do seem to involve pipes, so if used then the pipe writes will be atomic, and you won't get interleaving.

bazza
  • 7,580
  • 15
  • 22
  • 1
    I don't think that things written to stdout/stderr end up at logcat. Maybe tweaking some settings do this, but at default, it doesn't happen. Or this needs a new android version? – geza Jul 23 '17 at 12:14
  • @geza, your information is almost certainly more reliable than mine. Not being an Android-ist (I'm more of a Linxer) I'm not overly familiar with the matter, other than it seems that stdout and stderr normally go to /dev/nul, but can be redirected to the log. If that connection ultimately boils down to an IPC pipe, everything might be OK. I've added more to the answer. – bazza Jul 23 '17 at 12:42