Code...


#!/bin/bash

if [[ x$1 = x ]]; then
    f=${1:-/proc/${$}/fd/0}
else
    f=$1
fi

while read line
do
    echo $line
done < $f


[ Tested on Android 4.4_r1 with 3.4 goldfish kernel ]


android NDK의 경우 alloc하지 않은 메모리를 free하더라도 allocate된 영역이라면, 에러(segmentation fault)를 발생시키지 않는것 같다.

물론 그렇다고해서 정상적으로 free된다는 뜻은 아니다. 아래의 code와 비슷한  형태로 test code를 만들고, 실험해 보면, 메모리 leak이 발생하고 있음을 쉽게 알 수 있다.

test code는 아래와 같다.

#include <stdio.h> #include <stdlib.h> int main(int argc, const char *argv) { char *p = malloc(4096 * 5); p += 2 * 4096; free(p); // <= error가 발생하지 않음. 그렇다고 해서 free되는 것도 아님. p+=10000000; free(p); // <= "[1] + Stopped (signal)" 발생... (이게 기대했던 건데...) return 0; }

쩝....

이건, Device의 libc에서 지원해 줘야 하는데, bionic의 dlmalloc compile option에서 'DEBUG' option을 켤 경우, 이 문제가 해결된다...

뭐.. 속도를 위해서 희생한 거니... 어쩔 수 없다지만... 그래도 아쉽긴... 아쉽다...

<< DEBUG switch 켜기 >>

diff --git a/libc/upstream-dlmalloc/malloc.c b/libc/upstream-dlmalloc/malloc.c
index 3ef9b61..9efc27d 100644
--- a/libc/upstream-dlmalloc/malloc.c
+++ b/libc/upstream-dlmalloc/malloc.c
@@ -520,7 +520,7 @@ MAX_RELEASE_CHECK_RATE   default: 4095 unless not HAVE_MMAP
   disable, set to MAX_SIZE_T. This may lead to a very slight speed
   improvement at the expense of carrying around more memory.
 */
-
+#define DEBUG 1 /* YHCHO test */
 /* Version identifier to allow people to support multiple versions */
 #ifndef DLMALLOC_VERSION
 #define DLMALLOC_VERSION 20806


지급이 높아질 수록 다른 사람에게 책임/권한을 부여해야할 일이 생기게 된다. (혼자서 모든 일을 할 수 없으므로, 누군가에게 일을 맡겨야 한다.)

이때, 조직의 공식적인 관계(결재/인사평가 에 직접적인 영향을 주지 못하는 관계 - ex. 직급에 차이가 있다 하더라도, 팀장/팀원의 관계가 아닌, 팀원/팀원의 관계)로 맺어지지 않은 사람들을 한 그룹으로 묶어야 하는 경우가 종종 생기게 된다.

예를 들어 아래와 같은 상황을 가정해 보자.

ex. 팀장(수석): A책임 B 선임 데리고 이 일좀 맡아서 해!


이때, A, B는 모두 팀장에게 평가를 받는 같은 위치의 팀원일 뿐이다. 비록 한명은 책임이고, 한명은 선임일 지라도...


아래와 같은 세 가지 경우를 생각해 보자.

경우 1. A에게만 위와 같은 이야기를 전하고, A에게 맡겨 둔다.

경우 2. A, B가 같이 있는 자리에서 두 사람에게 동시에 이 이야기를 전달한다.

경우 3. 팀 회의에서 이야기를 전달한다.


세가지 경우, 모두 특별한 문제가 생기지는 않겠으나, '1' 보다는 '2', '2' 보다는 '3'의 경우가 A에게 '힘'을 실어준다는 측면에서 더 나아 보인다.

그리고, 이 후 팀장이 B와 개인적인 대면을 통해서, A에 대한 칭찬(비록 약간 과장 되었다고 할지라도) - 'A는 충분히 B를 리드할 만 하고, 따를만한 가치가 있는 사람이다.' 라는 생각을 B에게 심어 주기 위해 - 과 함께 , A를 잘 따르라는 이야기를 전달함으로서 더욱 A에게 힘을 실어 줄 수 있다.

또한, 필요하다면, '팀장이 B에 대한 인사평가에 A의 의견을 적극 반영하겠다.'라는 이야기까지 진행함으로써 , A가 B와 함께 일함에 있어서 생길 수 있는 관계상의 문제에 마침표를 찍을 수도 있을 것이다.


어찌보면, 회사에서 '인사평가 line 혹은 report line으로 대표되는 공식적인 관계'를 만들어 두는 것 역시 위에서 언급한 '팀장' 이 A에게 힘을 실어주는 방식과 원칙적인 측면에서 다르지 않다고 볼 수 있다. 즉, 회사차원에서 힘을 실어주는 행위일 것이다.

다만, 여기서 언급하고자 하는 것은, 조직 전체라는 큰 그림에서만 이런 '힘을 실어주는 행위'가 필요한 것이 아니라, 작은 조직, 크게는 '서열'이라는 관계를 형성하는 모든 경우에, 위와 같은 '힘을 실어주는 행위'가 필요하다는 점이다.


단! 여기서 주의할 점이 하나 있다. 위의 예시의 경우에서, "A책임이 팀장의 편애"를 받고 있다고 B선임이 생각해 오고 있었다면, 위와 같은 "힘을 실어주기"는 오히려 '반발'을 불러 올 수도 있다!


selectpollpipe등에 대해서 block하면서 event(읽을 수 있게 되는 상태)를 기다리기 위해서 많이 쓴다.

man page를 보면...

       select()  and pselect() allow a program to monitor multiple file descriptors, waiting until
       one or more of the file descriptors become "ready" for some class of I/O  operation  (e.g.,
       input  possible).   A  file descriptor is considered ready if it is possible to perform the
       corresponding I/O operation (e.g., read(2)) without blocking.

그렇지만 자칫 오해하면, 일반 파일에 대해서 새로 추가된 내용이 있는지 검사하고 새로 추가된 내용을 읽어들이기 위해서 selectpoll을 사용할 수 있을 거라고 기대할 수도 있다.

그렇지만, select/poll은 man page에 언급한 바처럼, "IO가 block되지 않고 IO를 행할 수 있는 상태"를 기다린다.

file descriptorread() operation을 통해서 EOF에 도달하더라도, read() operation은 blocking없이 계속해서 수행가능하다는 점 - 비록 계속 0 byte를 읽겠지만... - 을 생각해보면, 위와 같은 생각은 적합하지 않다는 것을 알 수 있다.

주의하자!


아래는 간단한 test code이다. 1초마다 계속해서 read() 가 정상 수행됨을 알 수 있다.

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <error.h>
#include <errno.h>

int
main(int argc, const char *argv[]) {
	char buf[4096];
	fd_set rfds;
	struct timeval tv;
	int fd, rb, retval;

	if (-1 == (fd = open("tstf", O_RDONLY)))
		perror("open tstf");

	/* Wait up to five seconds. */
	tv.tv_sec = 60;
	tv.tv_usec = 0;

	while (1) {
		/* Watch stdin (fd 0) to see when it has input. */
		FD_ZERO(&rfds);
		FD_SET(fd, &rfds);

		retval = select(fd + 1, &rfds, NULL, NULL, &tv);
		/* Don't rely on the value of tv now! */

		if (retval == -1)
			perror("select()");
		else if (retval) {
			printf("Data is available now.\n");
			memset(buf, 0, sizeof(buf));
			if (-1 == (rb = read(fd, buf, sizeof(buf) - 1)))
				fprintf(stderr, "%s", strerror(errno));
			printf("%s", buf);
			if (rb < (sizeof(buf) - 1))
				printf("\n---------------------------\nReaches to EOF\n");

			/* FD_ISSET(0, &rfds) will be true. */
		} else
			printf("No data within 1 minutes.\n");
		sleep(1);
	}
	exit(EXIT_SUCCESS);
}

DONE.

아래의 간단한 layout xml을 보자...


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:paddingBottom="@dimen/activity_vertical_margin"

    android:paddingLeft="@dimen/activity_horizontal_margin"

    android:paddingRight="@dimen/activity_horizontal_margin"

    android:paddingTop="@+id/button" <=== 여기

    tools:context=".MainActivity" >

    <Button

        android:id="@+id/button"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="say hello" />

</RelativeLayout>



자... paddingTop에는 당연히 dimension이 들어가야 한다. 그런데... 일부로 엉뚱하게도 '@+id/button'을 넣어봤다.

그런데.. compile error가 발생하지 않는다.!

resource compiler가 실제 id를 찾아서 id의 type을 읽어서 판단해 주지는 못하는 것으로 보인다.

대신 runtime에 아래와 같은 error를 발생시킨다.


11-09 02:47:49.750: E/AndroidRuntime(975): FATAL EXCEPTION: main

11-09 02:47:49.750: E/AndroidRuntime(975): Process: com.example.ttstest, PID: 975

11-09 02:47:49.750: E/AndroidRuntime(975): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.ttstest/com.example.ttstest.MainActivity}: android.view.InflateException: Binary XML file line #1: Error inflating class android.widget.RelativeLayout

11-09 02:47:49.750: E/AndroidRuntime(975): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2176)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2226)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.app.ActivityThread.access$700(ActivityThread.java:135)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1397)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.os.Handler.dispatchMessage(Handler.java:102)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.os.Looper.loop(Looper.java:137)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.app.ActivityThread.main(ActivityThread.java:4998)

11-09 02:47:49.750: E/AndroidRuntime(975): at java.lang.reflect.Method.invokeNative(Native Method)

11-09 02:47:49.750: E/AndroidRuntime(975): at java.lang.reflect.Method.invoke(Method.java:515)

11-09 02:47:49.750: E/AndroidRuntime(975): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:777)

11-09 02:47:49.750: E/AndroidRuntime(975): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:593)

11-09 02:47:49.750: E/AndroidRuntime(975): at dalvik.system.NativeStart.main(Native Method)

11-09 02:47:49.750: E/AndroidRuntime(975): Caused by: android.view.InflateException: Binary XML file line #1: Error inflating class android.widget.RelativeLayout

11-09 02:47:49.750: E/AndroidRuntime(975): at android.view.LayoutInflater.createView(LayoutInflater.java:620)

11-09 02:47:49.750: E/AndroidRuntime(975): at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.view.LayoutInflater.onCreateView(LayoutInflater.java:669)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:694)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.view.LayoutInflater.inflate(LayoutInflater.java:469)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.view.LayoutInflater.inflate(LayoutInflater.java:397)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.view.LayoutInflater.inflate(LayoutInflater.java:353)

11-09 02:47:49.750: E/AndroidRuntime(975): at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:290)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.app.Activity.setContentView(Activity.java:1928)

11-09 02:47:49.750: E/AndroidRuntime(975): at com.example.ttstest.MainActivity.onCreate(MainActivity.java:23)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.app.Activity.performCreate(Activity.java:5243)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2140)

11-09 02:47:49.750: E/AndroidRuntime(975): ... 11 more

11-09 02:47:49.750: E/AndroidRuntime(975): Caused by: java.lang.reflect.InvocationTargetException

11-09 02:47:49.750: E/AndroidRuntime(975): at java.lang.reflect.Constructor.constructNative(Native Method)

11-09 02:47:49.750: E/AndroidRuntime(975): at java.lang.reflect.Constructor.newInstance(Constructor.java:423)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.view.LayoutInflater.createView(LayoutInflater.java:594)

11-09 02:47:49.750: E/AndroidRuntime(975): ... 23 more

11-09 02:47:49.750: E/AndroidRuntime(975): Caused by: java.lang.UnsupportedOperationException: Can't convert to dimension: type=0x12

11-09 02:47:49.750: E/AndroidRuntime(975): at android.content.res.TypedArray.getDimensionPixelSize(TypedArray.java:464)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.view.View.<init>(View.java:3560)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.view.View.<init>(View.java:3475)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.view.ViewGroup.<init>(ViewGroup.java:464)

11-09 02:47:49.750: E/AndroidRuntime(975): at android.widget.RelativeLayout.<init>(RelativeLayout.java:236)

11-09 02:47:49.750: E/AndroidRuntime(975): ... 26 more



쩝... layout을 작성할때는.. 항상 조심하자...

compiler가 이런걸 걸러주지 못하니... 조심스럽게 작성해야 할 수 밖에...

Creating libraries - Static library, shared library

NOTE : In case jni shared library, 'jni' code should in shared library source code. That is, you can put all other source codes in static library, but 'jni' should NOT in static library source

[ Android.mk ] for libraries for NDK.

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS) LOCAL_MODULE := libX LOCAL_SRC_FILES := src0.c src1.c ... LOCAL_CFLAGS += LOCAL_C_INCLUDES += include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := libXS LOCAL_SRC_FILES := jni.c LOCAL_CFLAGS += LOCAL_C_INCLUDES += LOCAL_STATIC_LIBRARIES := libX include $(BUILD_SHARED_LIBRARY)

Now you can find

shared library in <project root>/libs/armeabi/

static library in <project root>/obj/local/armeabi/



Creating executable with prebuilt static library (ex. static library built above - libX.a)


[ Android.mk ] for libraries for NDK.

LOCAL_PATH := $(call my-dir) ####################################################### # ####################################################### include $(CLEAR_VARS) LOCAL_MODULE := myexe LOCAL_SRC_FILES := main.c LOCAL_CFLAGS := LOCAL_LDFLAGS := LOCAL_C_INCLUDES += LOCAL_LDLIBS := -L <example/above/libX/project/root>/obj/local/armeabi/ -lX include $(BUILD_EXECUTABLE)

Now you can get 'myexe' executable.


+ Recent posts