2011年7月19日 星期二

小筆電的效能不該只看數字

跟一位在 Acer 工作的同學閒聊,跟他說前幾天我接觸了一台 ASUS EEEPC 的小筆電,只是一台要價快兩萬,使用後覺得效能還不錯,問看看他的意見,結果就被推薦一款 Acer 小筆電,要價不到一萬五,並且說 Acer 這台小筆電是採用 Intel® Celeron® 處理器,效能應該比我指的那台還好,因為 ASUS 那台是 Intel® Atom™ 處理器。只是我很不解的是前者 Intel® Atom™ 處理器 D525 (1.8GHz) 且 2 cores, 4 threads 的,總覺得比後者 Intel® Celeron® 處理器 U3600 (1.2GHz) 之 2 cores, 2 threads 來的好吧?


後來在網路上也找到評論,大部分都說 U3600 比 D252 好,於是終於找到一個網站還滿豐富的 cpu benchmarks 資料:


PassMark - CPU Benchmarks - List of Benchmarked CPUs


可以查到 U3600 的分數比 D252 來的高 XD 算是稍微有點數據來證明,只是嚴格的話應該還是要去考量測試的方式及環境等等。


經過這次的討論,讓我想起計算機組織的教授曾提過的事,當你買一台車的時候,不太會用多少馬力來評估,那為何要用 CPU 的時脈來評估效能呢?並且查看了 spec 後,覺得 CPU 的規劃的確都很不一樣:


Intel® Atom™ processor D525 (1M Cache, 1.80 GHz)


Max TDP:13 W


Intel® Celeron® Processor U3600 (2M Cache, 1.20 GHz)


Max TDP:18 W


Atom 應該是著重在省電吧?還有超省電的:


Intel® Atom™ Processor N270 (512K Cache, 1.60 GHz, 533 MHz FSB)


Max TDP:2.5 W


至於要買哪一台比較好,大概還是要試用過才知道 :P 現在對 ASUS 那台算用過,所以稍微有好感,雖然真的貴很多 XD 至於兩者 CPU 差了 5W 的部分,不曉得是不是可以用 W=IV 公式來計算可以多撐多久?只是省電又不只 CPU 的問題 XD 看看別人的實測資料吧:


Nvidia ION 的 Asus 1215N 筆電評測


一台高階小筆電--宏碁AspireOne AO753小筆電開箱  (可以找到一些大陸的測試,但在此就不列出了)


2011年7月17日 星期日

Android 開發筆記 - 設定 Windows 之 Android NDK (Native Development Kit) 開發環境

之前的學習環境都是 Ubuntu 桌機為主,無聊在 ASUS Eee PC 1215N - Intel(R) Atom(TM) CPU D525 / 1.80GHz / 4GB RAM / Windows 7 32-bit 上,安裝 Eclipse 和 ADT 後,發現單獨啟動 Android 2.3.3 模擬器竟然只要 5 分鐘左右耶,這真是神奇了!(這個速度對這類裝備已經算很驚奇了),隨後我便開始著手看看在 Windows 使用 Android NDK 的環境了。


關於 NDK 的使用,採用 Cgywin 環境,如此一來就可以透過 ndk-build 進行編譯的動作了。


安裝項目:



  • Java Platform (JDK) - jdk-6u26-windows-i586.exe (76.8MB)

  • Eclipse Classic - eclipse-SDK-3.7-win32.zip (174MB)

  • Android SDK - installer_r12-windows.exe (34.8MB)

  • Android NDK - android-ndk-r6-windows.zip (64.5MB)

  • Cgywin - 安裝完大小約 225MB


    • 來源選 ftp://ftp.ntu.edu.tw (不行再改用 http://ftp.ntu.edu.tw)

    • 安裝項目:


      • 必要項目 Devel 分類:


        • gcc: C compiler upgrade helper

        • gcc-core: C compiler

        • gcc-g++: C++ compiler

        • gcc-mingw-core: Mingw32 support headers and libraries for GCC

        • gcc-mingw-g++: Mingw32 support headers and libraries for GCC C

        • make: The GNU version of the 'make' utility

        • mingw-runtime: MinGW Runtime



      • 非必要:

        • Net > openssh: The OpenSSH server and client programs

        • Editors > vim: ViIMproved - enhanced vi editor

        • Utils > screen: Run separate screens on a single terminal





    • 小問題:

      • 路徑上有空白的問題

      • 以 Eclipse 的 workspace 來說,預設位置是 C:\Users\username\workspace ,但在 cgywin 上也可以從 c:\Documents and Settings\username\workspace 進入,但從後者來說,路徑上有空白將導致 ndk-build 過程會出錯,請避開使用,若使用者帳號有中文或空白的話,那也盡量把 workspace 移去其他地方吧






上述軟體在 Windows 上的安裝路徑:


C:\Program Files\Java
C:\cygwin
C:\eclipse
C:\Android\android-sdk
C:\Android\android-ndk-r6


隨後在 Eclipse 建立一個 Project,此例為 MyNDK:


Project name: MyNDK
Build Target: Android 2.3.3/Android Open Source Project/2.3.3/10
Application name: MyNDK
Package name: com.example.ndk
Create Activity: MyNDK
Min SDK Version: 3


其他細節請參考 Android 開發教學筆記 - 使用 Android NDK (Native Development Kit) ,除此之外要留意該篇文章的軟體版本或路徑是不一樣的,僅供參考。


透過 Cygwin 測試 ndk-build:


user@pc ~
$ cd /cygdrive/c/Users/username/workspace/MyNDK/jni

user@pc /cygdrive/c/Users/username/workspace/MyNDK/jni
$ ls
Android.mk  my-jni.c

user@pc /cygdrive/c/Users/username/workspace/MyNDK/jni
$ cat Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    := my-jni
LOCAL_SRC_FILES := my-jni.c
include $(BUILD_SHARED_LIBRARY)

user@pc /cygdrive/c/Users/username/workspace/MyNDK/jni
$ cat my-jni.c
#include <string.h>
#include <jni.h>
jstring Java_com_example_ndk_MyNDK_stringFromJNI( JNIEnv* env, jobject thiz )
{
    return (*env)->NewStringUTF(env, "Hello from My JNI !");
}

user@pc /cygdrive/c/Users/username/workspace/MyNDK/jni
$ cd ..

user@pc /cygdrive/c/Users/username/workspace/MyNDK
$ /cygdrive/c/Android/android-ndk-r6/ndk-build
Compile thumb  : my-jni <= my-jni.c
SharedLibrary  : libmy-jni.so
Install        : libmy-jni.so => libs/armeabi/libmy-jni.so


接著可以設定 Eclipse 讓它可以自動編譯 JNI 的部分,在 Project > Properties > New > Program :


Location: C:\cygwin\bin\bash.exe
Working Directory: ${workspace_loc:${project_path}} (不設定也行,在此沒用到)
Arguments: --login -c "cd /cygdrive/c/Users/username/workspace/${project_path} && /cygdrive/c/Android/android-ndk-r6/ndk-build"


ndk-build-eclipse-cygwin


別忘了設定 Build options,例如 Run the builder 裡勾選 After a clean / During a manual builds 等,就可以透過 clean 來啟動 ndk build 動作。


如此一來,就可以透過 cgywin 裡頭,透過 vim 編輯器撰寫 jni 的程式部分,也可以在裡面呼叫 ndk-build 來測試,最後再切換到 Eclipse 以便測試整合囉。關於這次的 Windows Android NDK 的使用,原先以為多安裝個 cygwin 很麻煩,但仔細想一下我在 Ubuntu 上的使用,一樣也是透過開啟一個 terminal 去編輯 jni 的部分,那轉換在 Windows 上的動作,其實也沒有比較多。


相關文章:



2011年7月14日 星期四

Android 開發筆記 - 使用 NDK / JNI 實作從底層呼叫上層 (C call Java)

一般使用 JNI 的情境,不外呼從 Java 呼叫 C ,接著在 C (底層)運算完後,把數值透過 return 的方式傳回給 Java (上層)端,這在官方 NDK 教學或是之前的筆記都可以看到簡單的範例:Android 開發教學筆記 - 使用 Android NDK (Native Development Kit)。然而,如果要從 Native C 去呼叫 Java function 的話,就不是那麼直觀,例如在 Unix 系統上的程式開發,則是需取得一個門牌號碼(process id),接著才跟他溝通,或是直接透過執行新的程式,去指定運行某個 function 的這種架構等。在此使用的環境為 Ubuntu 10.04 desktop 64-bit,提供簡易 C call Java 的範例。


一般 C call Java 的話,則需要建立一個 Java 運行的 JVM 通道,接著才開始找尋物件、函數並開始運作:


@ c_to_java.c


#include <stdio.h>
#include <stdlib.h>
#include <jni.h>

int main()
{
JNIEnv *env;
JavaVM *jvm;

JavaVMInitArgs vm_args;
JavaVMOption options[1];

// launch Jvm
options[0].optionString = "-Djava.class.path=."; // add user classes
vm_args.version = JNI_VERSION_1_6; //JDK version.
vm_args.options = options;
vm_args.nOptions = 1;

if( JNI_CreateJavaVM(&jvm, (void*) &env, &vm_args) < 0 )
{
fprintf( stderr , "Launch JVM Error\n" );
exit(1);
}

// find the obj & method
jclass my_class;
jmethodID my_main;

if( !( my_class = (*env)->FindClass( env, "MyJavaClass") ) )
{
fprintf( stderr , "'Class' Not Found\n" );
exit(1);
}

if( !( my_main = (*env)->GetStaticMethodID( env, my_class , "main" ,  "([Ljava/lang/String;)V" ) ) )
fprintf( stderr , "'main' Not Found\n" );
else// Call main function
(*env)->CallStaticVoidMethod( env, my_class, my_main, NULL);

// finish
(*jvm)->DestroyJavaVM(jvm);

return 0;
}


@ MyJavaClass.java


class MyJavaClass
{
public static void main( String []arg)
{
System.out.println("Hello World");
}
}


@ Makefile


CC=gcc
INCLUDE=-I/usr/lib/jvm/java-6-sun/include/ -I/usr/lib/jvm/java-6-sun/include/linux/
LIB=/usr/lib/jvm/java-6-sun/jre/lib/amd64/server/libjvm.so

all:
        javac MyJavaClass.java
        $(CC) $(INCLUDE) $(LIB) c_to_java.c     
clean:
        rm -f ./a.out  ./*.class


@  /etc/ld.so.conf.d/jvm.conf 


/usr/lib/jvm/java-6-sun/jre/lib
/usr/lib/jvm/java-6-sun/jre/lib/amd64
/usr/lib/jvm/java-6-sun/jre/lib/amd64/server


執行結果:


$ make
$ ./a.out 
Hello World


由於我是在 Ubuntu 10.04 desktop 64-bit 環境,安裝 Sun Java6,所以 gcc 編譯要透過 -I 指定 header file 和 libjvm.so 檔案外,在執行 a.out 的時候也會出現找不到 share library 的情,這時候要去設定 /etc/ld.so.conf,在此是透過建立 /etc/ld.so.conf.d/jvm.conf 並再用 sudo ldconfig -v 進行 loading ,之後在執行 a.out 就不會出錯了


接著是 Android 端之 JNI 從底層 C 呼叫上層 Java 端的部分,稍稍不一樣,整個程式運作流程:



  1. 啟動 Android app 時,進入程式內的 OnCreate 時,將本身 app 的資訊丟進 C 底層,並使用全域變數紀錄。

  2. 由於 C 帶有 Java 端相關資訊,因此 C 可以呼叫 Java 端的相關函數,完成了底層往上層呼叫的動作。


建構於Android 開發教學筆記 - 使用 Android NDK (Native Development Kit) 的範例:


請留意 NDK 版本,此例是用 android-ndk-r5c ,用新版的話,相關路徑要記得更新


目錄結構:


~/android-ndk-r5c
~/workspace/MyNDK
~/workspace/MyNDK/jni
~/workspace/MyNDK/jni/my-jni.c
~/workspace/MyNDK/jni/Android.mk
~/workspace/MyNDK/jni/Makefile  (非必要)


@ MyNDK.java


package com.example.ndk;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import java.lang.ref.WeakReference;

public class MyNDK extends Activity {
    /** Called when the activity is first created. */
static int count;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.main);
        TextView tv = new TextView(this);
        tv.setText( stringFromJNI() );
        setContentView(tv);
        count = 0;
       
        initMyJNI(new WeakReference<MyNDK>(this));
    }

    private static void callback( int pid )
    {
        Log.i("MyNDK","Pid:"+pid+",Count:"+count);
    count++;
    }


    private native void initMyJNI( Object weak_this );

    public native String  stringFromJNI();

    static {
        System.loadLibrary("my-jni");
    }

}


@ my_jni.c


#include <stdio.h>
#include <string.h>
#include <jni.h>
#include <pthread.h>
#include <android/log.h>

#define LOG_TAG"MyJNI"
#define LOGV(...)__android_log_print( ANDROID_LOG_VERBOSE,LOG_TAG, __VA_ARGS__ )
#define LOGD(...)__android_log_print( ANDROID_LOG_DEBUG,LOG_TAG, __VA_ARGS__ )
#define LOGI(...)__android_log_print( ANDROID_LOG_INFO,LOG_TAG, __VA_ARGS__ )
#define LOGW(...)__android_log_print( ANDROID_LOG_WARN,LOG_TAG, __VA_ARGS__ )
#define LOGE(...)__android_log_print( ANDROID_LOG_ERROR,LOG_TAG, __VA_ARGS__ )

// --- Header Begin ---
#defineJAVA_CALLBACK_FUNCTION_NAME"callback"
#defineJAVA_CALLBACK_FUNCTION_ARGS_TYPE"(I)V"

JNIEXPORT void JNICALL Java_com_example_ndk_MyNDK_initMyJNI( JNIEnv* env, jobject thiz, jobject weak_this );
void * threadJobRun();

static const char *classPathName = "com/example/ndk/MyNDK";
static JNINativeMethod methods[] = {
{
"initMyJNI" , 
"(Ljava/lang/Object;)V" , 
(void *)Java_com_example_ndk_MyNDK_initMyJNI

};

static JavaVM *jvm;// global for JVM init Info
static jobject mObject;// get the object
static jclass mClass;// get the class
static jmethodID mMethodID;// get the method

static pthread_t thread;

// --- End Of Header ---


static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;

if( !( clazz = (*env)->FindClass( env, className ) ) )
{
LOGE( "Unable to get the Class at registerNativeMethods" );
return JNI_FALSE;
}

if( ( (*env)->RegisterNatives( env, clazz, gMethods, numMethods ) ) < 0 )
{
LOGE( "Unable to register the methods at registerNativeMethods" );
return JNI_FALSE;
}
return JNI_TRUE;
}

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
LOGI("IN JNI_OnLoad");
JNIEnv *env;
if( ( (*vm)->GetEnv( vm, (void **)&env, JNI_VERSION_1_6 ) ) != JNI_OK )
{
LOGE( "Unable to get the env at JNI_OnLoad" );
return -1;
}
if (!registerNativeMethods( env, classPathName, methods, sizeof(methods) / sizeof(methods[0])))
{
LOGE( "Unable to register native methods at JNI_OnLoad" );
return -1;
}
jvm = vm;
return JNI_VERSION_1_6;
}

JNIEXPORT void JNICALL Java_com_example_ndk_MyNDK_initMyJNI( JNIEnv* env, jobject thiz, jobject weak_this )
{
jclass clazz;

mClass = NULL;
mObject = NULL;
mMethodID = NULL;

if( !( clazz = (*env)->GetObjectClass( env, thiz ) ) )
{
LOGE( "Unable to get the object class" );
return;
}
if( !( mClass = (*env)->NewGlobalRef( env, clazz ) ) )
{
LOGE( "Unable to get the class ref" );
return;
}
if( !( mObject = (*env)->NewGlobalRef( env, weak_this ) ) )
{
LOGE( "Unable to get the object ref" );
return;
}

if( !( mMethodID = (*env)->GetStaticMethodID( env, mClass, JAVA_CALLBACK_FUNCTION_NAME, JAVA_CALLBACK_FUNCTION_ARGS_TYPE ) ) )
{
LOGE( "Unable to get the method ref" );
return;
}

LOGI("IN initMyJNI");

if( mClass && mMethodID )// first call
(*env)->CallStaticVoidMethod( env, mClass , mMethodID , (int)getpid() );

// thread
pthread_create( &thread, NULL, threadJobRun, NULL);
}
void * threadJobRun()
{
JNIEnv *env;
int isAttached;

isAttached = 0;
env = NULL;
LOGI("IN threadJobRun");
if( jvm )
{
LOGI("IN threadJobRun with JVM");

if( ( (*jvm)->GetEnv( jvm, (void**) &env, JNI_VERSION_1_6 ) ) < 0 )
{
LOGI( "Unable to get env at threadJobRun" );
if( ( (*jvm)->AttachCurrentThread( jvm, &env, NULL) ) < 0 )
{
LOGE( "Unalbe to attach current thread at threadJobRun" );
return NULL;
}
isAttached = 1;
}

while( 1 )
{
sleep(1);

// --- jobs begin
if( mClass && mMethodID )
{
LOGI( "call mClass & mMehtodID" );
(*env)->CallStaticVoidMethod( env, mClass , mMethodID , (int)getpid() );
}
// --- end of jobs
}

if( isAttached )
{
(*jvm)->DetachCurrentThread( jvm );
}
}
return NULL;
}

jstring Java_com_example_ndk_MyNDK_stringFromJNI( JNIEnv* env, jobject thiz )
{
    return (*env)->NewStringUTF(env, "Hello from My JNI !");
}


@ Android.mk


LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := my-jni
LOCAL_SRC_FILES := my-jni.c

LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog

include $(BUILD_SHARED_LIBRARY)


@ Makefile (非必要)


all:
cd ../ && sh ~/android-ndk-r5c/ndk-build


其中 Makefile 是我自己工作上使用的,我習慣用 vim + makefile 開發,所以會需要這個檔案,所以此檔非必要,其他檔案如同 Android 開發教學筆記 - 使用 Android NDK (Native Development Kit) 敘述。比較重要的是 my-jni.c 裡頭,有一個 JNI_OnLoad 和 initMyJNI 函數,在 app 一開始執行後,會先透過 JNI_OnLoad 將相關的資訊紀錄在 global variables 中,如 JVM 以及相關動作等,接著在 Java 呼叫 initMyJNI 時,才正式將 Java Object 紀錄起來,例如 Class 、Method 等,另外,在 initMyJNI 時,已經算是做到 C call Java 之 callback 的動作了(first call),只是我的目標是在 C 層跑一個 thread ,等到有事件時去更新 Java 層,所以有多一個 thread 的運行,在 threadJobRun 中則是 thread 的動作囉。


程式運作過程中,我有透過 Logcat 印出點訊息,可以看得清楚:


DDMS_MyJNI


其中 tag 為 MyJNI 是指 C 那層的動作,可以看到流程是 IN JNI_OnLoad、IN initMyJNI,接著就是第一次 c call java,在 Java 層印出 Pid:853, Count:0 的訊息,接著才進入 Thread ,然後每秒印出一次 Pid:853, Count:number 等資訊。


其他比較重要的是 C 裡頭也能可以呼叫 logcat ,記得在 Android.mk 中要加入 LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog 才不會編譯失敗(擺放的位置也很重要,擺在上頭會失敗);而 c call java 的部分,在 Java 那邊定義的函數必須是 static 的,此例為 private static void callback( int pid ),不然也會出錯(因為 C 裡頭是用 GetStaticMethodID ),我有去找一下其他 open source (Kwaak3 / KwaakJNI.java),寫法也是這樣。


最後一提,上述 C 程式存在很多地方沒寫好,例如資源的釋放等,在此僅供用來了解流程用途。


參考資料:



2011年7月13日 星期三

iOS 開發教學 - 關於 Crash Report 和 dSYM 的使用

前幾天玩遊戲時,看到開發者請大家把 Crash Log / Crash Report / Bug Report 寄回給他,如此一來就可以偵錯,滿好奇的便也隨意在網路上找找該怎樣使用,在此便稍作筆記,並且寫一個簡單會 crash 的程式(很少人會這樣做吧 XD)。

所謂的 Crash Report 是在實體機器上,執行程式時發生 crash 時,此時 iOS 會幫忙將程式出錯的相關資訊存起來,這時候用 Xcode 的 Organizer - Devices 時,可以去點選設備,並且可以查看有哪些 report 在上頭,這時候就可以稍作觀看,但是上頭記錄的資訊是有稍微類似做過保護的,只會看到類似(這是網路上收集的資訊,但我嘗試做出的 log 好像已經轉好了?):

Thread 0 Crashed:
0   libSystem.B.dylib              0x35de3ad0 0x######## + ##
1   libSystem.B.dylib              0x35de3abe 0x######## + ##
2   libSystem.B.dylib              0x35de3ab2 0x######## + ##
3   libSystem.B.dylib              0x35dfad5e 0x######## + ##
4   libSystem.B.dylib              0x35de967e 0x######## + ##
5   libSystem.B.dylib              0x35de974c 0x######## + ##
6   libSystem.B.dylib              0x35d5c8e0 0x######## + ##
7   libSystem.B.dylib              0x35d5c798 0x######## + ##
8   CoreFoundation                 0x3750022c 0x######## + ##
9   CoreFoundation                 0x374fc206 0x######## + ##
10  CoreFoundation                 0x37504564 0x######## + ##
11  CoreFoundation                 0x37504406 0x######## + ##
12  Foundation                     0x351530f0 0x######## + ##
13  CrashTesting                   0x00002712 0x######## + ##
14  UIKit                          0x35924fd0 0x######## + ##
15  UIKit                          0x3591ea70 0x######## + ##
16  UIKit                          0x358d82dc 0x######## + ##
17  UIKit                          0x358d7b14 0x######## + ##
18  UIKit                          0x358d73ac 0x######## + ##
19  GraphicsServices               0x33e77c80 0x######## + ##
20  CoreFoundation                 0x3752f5c4 0x######## + ##
21  CoreFoundation                 0x3752f582 0x######## + ##
22  CoreFoundation                 0x3752182e 0x######## + ##
23  CoreFoundation                 0x37521504 0x######## + ##
24  CoreFoundation                 0x37521412 0x######## + ##
25  UIKit                          0x3591d54c 0x######## + ##
26  UIKit                          0x3591a550 0x######## + ##
27  CrashTesting                   0x0000266e 0x######## + ##
28  CrashTesting                   0x00002620 0x######## + ##

這時候需要使用原先編譯 app 時,在同一目錄中會產生對應的 dSYM 檔案,搭配 dwarfdump 使用才可以查詢到真正的片段程式(例如想看 line 13 的意思):

$ dwarfdump --lookup 0x00002712 -arch armv6 CrashTesting.app.dSYM/
----------------------------------------------------------------------
 File: CrashTesting.app.dSYM/Contents/Resources/DWARF/CrashTesting (armv6)
----------------------------------------------------------------------
Looking up address: 0x0000000000002712 in .debug_info... found!

0x00000132: Compile Unit: length = 0x000049b0  version = 0x0002  abbr_offset = 0x00000000  addr_size = 0x04  (next CU at 0x00004ae6)

0x0000013d: TAG_compile_unit [1] *
             AT_producer( "4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2373.6)" )
             AT_language( DW_LANG_ObjC )
             AT_name( "CrashTestingAppDelegate.m" )
             AT_entry_pc( 0x000026b4 )
             AT_stmt_list( 0x0000006b )
             AT_comp_dir( "/Volumes/Data/iPhoneDev/trunk/CrashTesting/CrashTesting" )
             AT_APPLE_major_runtime_vers( 0x02 )

0x00000166:     TAG_subprogram [16] *
                 AT_sibling( {0x000001d2} )
                 AT_name( "-[CrashTestingAppDelegate application:didFinishLaunchingWithOptions:]" )
                 AT_decl_file( "/Volumes/Data/iPhoneDev/trunk/CrashTesting/CrashTesting/CrashTestingAppDelegate.m" )
                 AT_decl_line( 17 )
                 AT_prototyped( 0x01 )
                 AT_type( {0x0000015b} ( BOOL ) )
                 AT_APPLE_isa( 0x01 )
                 AT_low_pc( 0x000026b4 )
                 AT_high_pc( 0x00002780 )
                 AT_frame_base( r7 )

0x000001b9:         TAG_lexical_block [5] *
                     AT_low_pc( 0x000026c4 )
                     AT_high_pc( 0x00002780 )
Line table file: 'CrashTestingAppDelegate.m' line 20, column 0 with start address 0x000000000000270e

Looking up address: 0x0000000000002712 in .debug_frame... found!

0x00000020: FDE
        length: 0x0000000c
   CIE_pointer: 0x00000000
    start_addr: 0x000026b4 -[CrashTestingAppDelegate application:didFinishLaunchingWithOptions:]
    range_size: 0x000000cc (end_addr = 0x00002780)
  Instructions: 0x000026b4: CFA=4294967295+4294967295

因此就可以查看到 line 13 是指 CrashTestingAppDelegate.m' line 20,所以就可以去那邊追查看看。

以上的例子僅需建立一個 Project (此例名為 CrashTesting),然後在 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 裡頭寫一段會 crash 的程式碼(此例為 double free):


    NSString *justCrash = [[NSString alloc] initWithFormat:@"CrashTesting"];
    [justCrash release];
    [justCrash release];

如此一來,在實機執行完並當機後,可以接上 Xcode 去 Organizer - Devices 查看:

xcode-crash-log

而 dSYM 檔案,則是在 Xcode 編譯的地方:

xcode-dSYM

點選後,就可以看到並且可以複製到習慣的工作目錄囉

xcode-app-dSYM

參考資料:


iPhone/iPad 遊戲 - 大家來搶錢 HD

大家來搶錢 HD-0 大家來搶錢 HD-1


前陣子趁著限時免費時,下載了這款遊戲(現在要價 1.99 美金)。後來過了一陣子後,同事一直在玩也推薦我玩,就不小心也陷入想要把地圖全破的慾念中。這款就像是兒時 PC 上的大富翁遊戲,可以選地圖、餐與人物,遊戲過程中藉由買土地、建房子、收過路費等模式進行,除此之外,有簡單的卡片系統,可以買特殊的卡片,如停留卡、購地卡、請神卡、均富卡等等,當然還有四個神仙供你參拜以及錢滾錢的股票系統。


大家來搶錢 HD-2


目前共提供 10 個地圖,玩起來還不錯,就跟 PC 上的大富翁很像,只是沒那麼複雜。這幾天開始試試股票系統,今天不小心就從 4 塊錢買進,遊戲進行中又使用了幾張紅卡後,目前市值已經有 99 元,使得 UI 顯示已經爆表看不到股票現值了 XD 然後定期會有股票現金股利吧?不知不覺手頭上的現金也蹦出一堆。我想,我應該可以正式從這款遊戲畢業了 XDD


大家來搶錢 HD-3


這款應該是香港或大陸人開發的遊戲吧?我覺得品質還不錯,隨著地圖有對應的風格背景、遊戲聲效等,最近好像有更新軟體?或許修正了一些問題或增加難度了吧。雖然我對遊戲開發還沒有熱情,但透過這款遊戲的刺激,哪天也來研究一下遊戲吧!總覺得這款是透過某種機制 porting 到 iPhone 上頭,如果也是走 OpenGL ES 的話,或許 Android 也能無痛 porting 呢。


最後一提,這款有支援 iPad 喔,算挺不賴的!