Home » Android » Redirect stdout to logcat in Android NDK

Redirect stdout to logcat in Android NDK

Posted by: admin April 23, 2020 Leave a comment

Questions:

I can’t get my Nexus S (running Android 4.0) to redirect native stdout message to logcat. I’ve read that I need to do this:

$ adb shell stop
$ adb shell setprop log.redirect-stdio true
$ adb shell start

However, this doesn’t seem to work. (It does break JUnit though, as mentioned here, so it’s not without effect.)

For reference, here’s my code:

package com.mayastudios;

import android.util.Log;

public class JniTester {

  public static void test() {
    Log.e("---------", "Start of test");
    System.err.println("This message comes from Java.");    
    void printCMessage();
    Log.e("---------", "End of test");
  }

  private static native int printCMessage();

  static {
    System.loadLibrary("jni_test");
  }
}

And the JNI .c file:

JNIEXPORT void JNICALL
Java_com_mayastudios_JniTester_printCMessage(JNIEnv *env, jclass cls) {
  setvbuf(stdout, NULL, _IONBF, 0);
  printf("This message comes from C (JNI).\n");
  fflush(stdout);

  //setvbuf(stderr, NULL, _IONBF, 0);
  //fprintf(stderr, "This message comes from C (JNI).\n");
  //fflush(stderr);
}

And the Android.mk:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := jni_test
LOCAL_SRC_FILES := test_jni.c
include $(BUILD_SHARED_LIBRARY)

I’m compiling this just by calling ndk-build. The native method is called correctly but I don’t get any log output from it (even on verbose). I do get the log output from Java though (“This message comes from Java.”).

Any hints of what I might be doing wrong?

PS: I’ve set up a small Mercurial repository that demonstrates the problem: https://bitbucket.org/skrysmanski/android-ndk-log-output/

How to&Answers:

This was not obvious to me from the previous answers. Even on rooted devices you need to run:

adb root
adb shell stop
adb shell setprop log.redirect-stdio true
adb shell start

Answer:

You should also be able to wrap your logging code to detect if it’s Android and if so use Android’s logging. This may or may not be practical for everyone.

Include the logging header file:

#include <android/log.h>

Use the built in logging functionality:

__android_log_print(ANDROID_LOG_INFO, "foo", "Error: %s", foobar);

Answer:

setprop log.redirect-stdio true redirects only output generated by java code, but not the native code. This fact is described on developer.android.com in following way:

By default, the Android system sends stdout and stderr (System.out and System.err) output to /dev/null. In processes that run the Dalvik VM, you can have the system write a copy of the output to the log file.

To get output from the native code, I really liked this solution

Answer:

stdout/stderr are redirected to /dev/null in Android apps. The setprop workaround is a hack for rooted devices that copies stdout/stderr to the log. See Android Native Code Debugging.

Answer:

Here the code I use to to output stdout and stderr to android log

#include <android/log.h>
#include <pthread.h>
#include <unistd.h>

static int pfd[2];
static pthread_t loggingThread;
static const char *LOG_TAG = "YOU APP LOG TAG";

static void *loggingFunction(void*) {
    ssize_t readSize;
    char buf[128];

    while((readSize = read(pfd[0], buf, sizeof buf - 1)) > 0) {
        if(buf[readSize - 1] == '\n') {
            --readSize;
        }

        buf[readSize] = 0;  // add null-terminator

        __android_log_write(ANDROID_LOG_DEBUG, LOG_TAG, buf); // Set any log level you want
    }

    return 0;
}

static int runLoggingThread() { // run this function to redirect your output to android log
    setvbuf(stdout, 0, _IOLBF, 0); // make stdout line-buffered
    setvbuf(stderr, 0, _IONBF, 0); // make stderr unbuffered

    /* create the pipe and redirect stdout and stderr */
    pipe(pfd);
    dup2(pfd[1], 1);
    dup2(pfd[1], 2);

    /* spawn the logging thread */
    if(pthread_create(&loggingThread, 0, loggingFunction, 0) == -1) {
        return -1;
    }

    pthread_detach(loggingThread);

    return 0;
}

Answer:

Try putting fflush(stdout); after your printf. Or alternatively use fprintf(stderr, "This message comes from C (JNI).\n");

stdout by default is buffering. stderr – is not. And you are using System.err, not System.out from Java.

Also you can disable stdout buffering with this: setvbuf(stdout, NULL, _IONBF, 0); Just make sure you call it before first printf call.