Open kong1z opened 4 years ago
NDK version: ndk-r10d
I have no idea how that's supposed to behave. Try again with r20 and lmk if you have problems. AFAIK coverage works with the process you described (though iirc GCOV_PREFIX_STRIP
depends on your build location, so that may need to be tweaked).
NDK version: ndk-r10d
I have no idea how that's supposed to behave. Try again with r20 and lmk if you have problems. AFAIK coverage works with the process you described (though iirc
GCOV_PREFIX_STRIP
depends on your build location, so that may need to be tweaked).
Configuration:
LOCAL_CFLAGS += -fprofile-arcs -ftest-coverage
LOCAL_CXXFLAGS += -fprofile-arcs -ftest-coverage
LOCAL_LDLIBS += --coverage
I used ndk-r20, but still don't generate .gcda files after the android app finishes running. I can't analyze code coverage without .gcda files.
Looks like the Android app links .so files, unable to analyze the code coverage of .so files. I don't know if it's a problem with ndk, maybe ndk doesn't have this feature.
In addition, what is lmk?
@pirama-arumuga-nainar is the expert on code coverage on Android...
("lmk" is short for "let me know".)
The profiles are written during exit() via an atexit callback. But Android apps usually get killed by the framework and don't get to run the atexit callbacks. The app should instead call llvm_gcov_flush() to explicitly write the coverage files to disk. You may also need to set GCOV_PREFIX to some path that's accessible/writable from the app.
The profiles are written during exit() via an atexit callback. But Android apps usually get killed by the framework and don't get to run the atexit callbacks. The app should instead call llvm_gcov_flush() to explicitly write the coverage files to disk. You may also need to set GCOV_PREFIX to some path that's accessible/writable from the app.
First of all, thank everyone for their help.
The llvm_gcov_flush() function was not found, so I used __llvm_gcov_flush().
Now my operation is like this: Still using ndk-r20. Created a very simple android demo, the interface has two buttons: btn_init and btn_flush. Clicking btn_init will set the path:
// The " /data/local/tmp" path is accessible and writable, and my phone is also root.
Setenv("GCOV_PREFIX", "/data/local/tmp", 1);
setenv("GCOV_PREFIX_STRIP", "100", 1);
Clicking btn_flush will call:
__llvm_gcov_flush();
package com.example.konglingzeng.demo;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.system.Os;
import android.view.View;
import android.widget.Toast;
import java.io.File;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
System.loadLibrary("Java2C");
Toast.makeText(MainActivity.this, "loadLibrary", Toast.LENGTH_LONG).show();
// new File(android.os.Environment.getExternalStorageDirectory() + "/data_root_directory/gcda/").mkdir();
// try{
// Os.setenv("GCOV_PREFIX", android.os.Environment.getExternalStorageDirectory() + "/data_root_directory/gcda/", true);
// Os.setenv("GCOV_PREFIX_STRIP", "10", true);
// }
// catch(Exception e){
// e.printStackTrace();
// }
// Example of a call to a native method
findViewById(R.id.btn_init).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String input_str = "";
String res = new Java2CJNI().init(input_str);
Toast.makeText(MainActivity.this, res, Toast.LENGTH_LONG).show();
}
});
findViewById(R.id.btn_flush).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Java2CJNI().flush();
}
});
}
}
package com.example.konglingzeng.demo;
public class Java2CJNI {
public native String init(String str);
public native void flush();
}
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := Java2C
LOCAL_SRC_FILES := java2C.cc
LOCAL_LDLIBS += -lm -llog -landroid
LOCAL_CFLAGS += -fprofile-arcs -ftest-coverage
LOCAL_CXXFLAGS += -fprofile-arcs -ftest-coverage
#LOCAL_LDFLAGS += -lgcov
LOCAL_LDLIBS += --coverage
#LOCAL_LDFLAGS += -fprofile-dir=/data/local/tmp/
include $(BUILD_SHARED_LIBRARY)
APP_PLATFORM := android-16
APP_ABI := armeabi-v7a
APP_STL := c++_static
APP_OPTIM := debug
com_example_konglingzeng_demo_Java2CJNI.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_konglingzeng_demo_Java2CJNI */
#ifndef _Included_com_example_konglingzeng_demo_Java2CJNI
#define _Included_com_example_konglingzeng_demo_Java2CJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_konglingzeng_demo_Java2CJNI
* Method: init
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_konglingzeng_demo_Java2CJNI_init
(JNIEnv *, jobject, jstring);
/*
* Class: com_example_konglingzeng_demo_Java2CJNI
* Method: flush
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_example_konglingzeng_demo_Java2CJNI_flush
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
#include "com_example_konglingzeng_demo_Java2CJNI.h"
#include <stdlib.h>
#include <signal.h>
#include <android/log.h>
#include <setjmp.h>
#include <pthread.h>
#include <dlfcn.h>
#include <stdio.h>
#define TAG "JNITEST"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)
//extern "C" void __gcov_flush();
extern "C" void __llvm_gcov_flush();
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jstring JNICALL Java_com_example_konglingzeng_demo_Java2CJNI_init(JNIEnv *env, jobject obj, jstring path)
{
LOGE("===============================JNI_init");
setenv("GCOV_PREFIX", "/data/local/tmp", 1);
//setenv("GCOV_PREFIX", "/storage/emulated/0/data_root_directory/gcda", 1);
setenv("GCOV_PREFIX_STRIP", "100", 1);
return env->NewStringUTF("From native !");
}
JNIEXPORT void JNICALL Java_com_example_konglingzeng_demo_Java2CJNI_flush(JNIEnv *env, jobject obj)
{
LOGE("==============================JNI_flush");
__llvm_gcov_flush();
//__gcov_flush();
}
#ifdef __cplusplus
}
#endif
/data/local/tmp
isn't going to be accessible from an app, you'll need to use the app's directory (e.g. /data/data/com.example.konglingzeng.demo
), and pull it (either with root adb, or run-as with something like adb shell run-as com.example.konglingzeng.demo tar cf - /data/data/com.example.konglingzeng.demo | tar xfv -
).
/data/local/tmp
isn't going to be accessible from an app, you'll need to use the app's directory (e.g./data/data/com.example.konglingzeng.demo
), and pull it (either with root adb, or run-as with something likeadb shell run-as com.example.konglingzeng.demo tar cf - /data/data/com.example.konglingzeng.demo | tar xfv -
).
Yes! ! ! Generate *.gcda files! ! !
/data/local/tmp
isn't going to be accessible from an app !!!
We don't currently have any docs for coverage, so I'm going to reopen this to track that.
Expected scenario:
Compile a *.so with ndk and integrate it into the apk. Expect to generate a .gcda file when running the app on Android, and then analyze the code coverage of c++.
NDK version: ndk-r10d
Ndk compile *.so operation:
Set the compile link parameters as follows:
Set the path where .gcda is stored (the path can be written):
The .gcno file is generated normally when compiling and generating so. Then I integrated *.so into the Android apk.
There is information on the Internet that the program should be completed normally and cannot be killed. So I force __gcov_flush() to be called, and I make sure this function is called.
The end result is that the .gcda file cannot be generated after the program is run on an Android phone.
So, does ndk support code coverage? If it is supported, where is the problem?
I have nowhere else to find useful information. Looking forward to answering, thank you.