2012年5月26日 星期六

[LLVM] LLVM、Clang、Clang Plugin、Clang Rewriter 筆記 @ Ubuntu 64bit

Source: http://en.wikipedia.org/wiki/LLVM
圖片來源:http://llvm.org/Logo.html


第一次看到 Open source project 覺得 LOGO 很殺 XD 這也代表 LLVM 真的有某個層度的威力!LLVM 全名為 Low Level Virtual Machine,翻譯為底層虛擬機器,並且從 wiki 介紹可以略知一二,就是透過虛擬技術來作最佳化。



  • http://llvm.org/

  • http://en.wikipedia.org/wiki/LLVM


    • LLVM (formerly Low Level Virtual Machine) is a compiler infrastructure written in C++ that is designed for compile-time, link-time, run-time, and "idle-time" optimization of programs written in arbitrary programming languages.


  • http://zh.wikipedia.org/zh-tw/LLVM


    • LLVM ,命名最早源自於底層虛擬機器(Low Level Virtual Machine)的縮寫[1]。它是一個編譯器的基礎建設,以C++寫成。它是為了任意一種程式語言寫成的程式,利用虛擬技術,創造出編譯時期,鏈結時期,執行時期以及「閒置時期」的最佳化。



談論到虛擬機器,對資訊領域最親近的程式語言是 Java,以及 Java bytecode、Java Virtual Machine (JVM) 和跨平台的關係。將 Java 程式碼 (.java) 編譯成 bytecode (.class) 後,透過 JVM 可執行結果,加上多種平台都有 JVM (Windows/Linux/Mac),提供開發者只需撰寫一份程式碼、僅需編譯一次,接著就可以拿著編譯完的結果到各類有支援 JVM 的平台執行,成果就達成了跨平台。若粗淺地看 LLVM 的話,也有類似的關係,只是這時架構稍微調整成:


N 種程式語言 (front ends) > 產生 LLVM Bitcode > 產生 M 個程式執行環境 (back ends)


而 LLVM 能做的事不止於此,還可進行程式效能最佳化處理等等。


首先提一下安裝 LLVM 的部分好了,參考官方文件的介紹,在此選擇 Ubuntu 64bit 環境:


$ uname -a
Linux 2.6.32-38-server #83-Ubuntu SMP Wed Jan 4 11:26:59 UTC 2012 x86_64 GNU/Linux
$ dmesg | grep -i cpu
Intel(R) Xeon(R) CPU E5420 @ 2.50GHz

下載程式碼:


$ cd ~/ && svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm
$ cd ~/llvm/tools/ && svn co http://llvm.org/svn/llvm-project/cfe/trunk clang
$ cd ~/llvm/projects && svn co http://llvm.org/svn/llvm-project/compiler-rt/trunk compiler-rt
$ cd ~/llvm/projects && svn co http://llvm.org/svn/llvm-project/test-suite/trunk test-suite

採用官方編譯方式:


$ cd ~/ && mkdir -p build out && cd ~/build && ../llvm/configure --prefix=$HOME/out
$ time make -j8
real 9m55.952s
user 47m7.270s
sys 5m49.610s

採用 cmake 編譯方式:


$ sudo apt-get install cmake
$ cd ~/ && mkdir -p build out && cd ~/build && cmake -DCMAKE_INSTALL_PREFIX:PATH=$HOME/out ../llvm
$ time make -j8
real 5m39.994s
user 37m23.290s
sys 4m27.790s

原先是某次用 configure 編譯會過不了(trunk版本常碰到的現象),就嘗試用 CMAKE ,結果一用發現編得更快,從此我就都用 CMAKE 了 XDDD (或許有少編啥東西 :P 但不影響我研究,就沒計較了) 須留意編譯 Clang plugin 的部分,如果 llvm 採用 configure 編法,那只需切換到 clang plugin project 中執行 make 即可,其他細節可參考 PrintFunctionNames 裡的 README,如果採用 CMAKE 編法,記得要跑去 CMake 產生得相對目錄結構中,執行 make 才會編出來,並且編出來的位置是擺在在 lib 中(此例是~/build/lib)。


研究一下 clang 的用處好了,最直觀的用途是像 gcc 的編譯器功能(並非全部功能都支援, 這跟LLVM架構有關),把 source code 編譯成執行檔,而 compiler 有很多種,例如 one-pass 跟 multi-pass compiler,我想 LLVM 架構應該算 multi-pass 的,由 clang 作為 front end 的部份,將程式碼編譯成 LLVM Bitcode,接著在經由 LLVM 架構,搭配 back ends 端產出各種平台的 native code。而 clang 的功能不只於此,參考 Google 發表的相關文章 C++ at Google: Here Be Dragons,可以透過 clang 來處理一些錯誤偵錯,例如 bool *charge_acct 變數,操作上不小心弄成 charge_acct = false 的問題(正確使用應該是 charge_acct = NULL 或 *charge_acct = false),更多介紹請參考 Chromium Style Checker Errors。簡言之,透過 clang 可以幫忙掃 source code 找一些容易忽略的錯誤,並且輸出人性化的訊息。相關產品: Clang Static Analyzer


由於 Clang 可以分析 source code,那就存在很多玩法,且架構上提供 clang plugin 用法。範例程式:clang/examples/PrintFunctionNames/PrintFunctionNames.cpp,翻閱一下,可以看到一些關鍵字:PluginASTActionASTConsumer,其中 AST 在 Compiler 界全名為 Abstract Syntax Tree,就是把程式碼建立一個 tree 結構,方便分析。


至於寫一個 Clang Plugin 要做的事:(可從範例程式 PrintFunctionNames 複製一份再來修改)



  1. 撰寫 PluginASTAction


    • 關於 PluginASTAction 架構可參考 http://clang.llvm.org/doxygen/classclang_1_1PluginASTAction.html, 簡言之,包括 CreateASTConsumer 與 ParseArgs,其中 ParseArgs 可透過參數來初始化 plugin 操作環境或是區分要執行那個動作,而 CreateASTConsumer 則是核心項目,也就是依工作目的建立自己的 ASTConsumer。


  2. 撰寫 ASTConsumer


  3. 註冊操作時使用的名稱


    • 此 PrintFunctionNames 為 print-fns


  4. 編譯產生 xxx.so 或在 Mac 為 xxx.dylib 檔


    • 此 PrintFunctionNames  為 PrintFunctionNames.so 或 libPrintFunctionNames.so


  5. 操作使用 clang -cc1 -load /path/xxx.so -plugin YourPluginName YourInputFile 或 clang -cc1 -load /path/xxx.so -plugin YourPluginName -plugin-arg-YourPluginName YourArgs YourInputFile


    • clang -cc1 -load /path/PrintFunctionNames.so -plugin print-fns some-input-file.c

    • clang -cc1 -load /path/PrintFunctionNames.so -plugin print-fns -plugin-arg-print-fns -an-error some-input-file.c



在 PrintFunctionNames 例子來說,在 PluginASTAction::ParseArgs 有一個簡易判斷參數的架構;在 ASTConsumer 裡頭,使用 HandleTopLevelDecl 功能,用來輸出程式碼內的 top level function name。因此,若要寫 Clang Plugin 的話,可以從 PrintFunctionNames 的架構修改。接著更複雜的應用就是 source-to-source translation,可用 Google 搜尋 clang source to source translation 或 clang rewriter 看看,最經典的例子是 lib/Rewrite/RewriteObjC.cpp


使用 clang rewriter 進行 source to source translation,將 ObjC 程式碼轉成 C 語言:


$ sudo apt-get install llvm clang g++
$ vim Hello.m
#import
int main( int argc, const char *argv[] ) {
printf( "hello world\n" );
return 0;
}
$ clang -rewrite-objc Hello.m
$ cat Hello.cpp
#ifndef __OBJC2__
#define __OBJC2__
#endif
struct objc_selector; struct objc_class;
struct __rw_objc_super {
struct objc_object *object;
struct objc_object *superClass;
__rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {}
};
#ifndef _REWRITER_typedef_Protocol
typedef struct objc_object Protocol;
#define _REWRITER_typedef_Protocol
#endif
#define __OBJC_RW_DLLIMPORT extern
__OBJC_RW_DLLIMPORT void objc_msgSend(void);
__OBJC_RW_DLLIMPORT void objc_msgSendSuper(void);
__OBJC_RW_DLLIMPORT void objc_msgSend_stret(void);
__OBJC_RW_DLLIMPORT void objc_msgSendSuper_stret(void);
__OBJC_RW_DLLIMPORT void objc_msgSend_fpret(void);
__OBJC_RW_DLLIMPORT struct objc_class *objc_getClass(const char *);
__OBJC_RW_DLLIMPORT struct objc_class *class_getSuperclass(struct objc_class *);
__OBJC_RW_DLLIMPORT struct objc_class *objc_getMetaClass(const char *);
__OBJC_RW_DLLIMPORT void objc_exception_throw( struct objc_object *);
__OBJC_RW_DLLIMPORT void objc_sync_enter( struct objc_object *);
__OBJC_RW_DLLIMPORT void objc_sync_exit( struct objc_object *);
__OBJC_RW_DLLIMPORT Protocol *objc_getProtocol(const char *);
#ifndef __FASTENUMERATIONSTATE
struct __objcFastEnumerationState {
unsigned long state;
void **itemsPtr;
unsigned long *mutationsPtr;
unsigned long extra[5];
};
__OBJC_RW_DLLIMPORT void objc_enumerationMutation(struct objc_object *);
#define __FASTENUMERATIONSTATE
#endif
#ifndef __NSCONSTANTSTRINGIMPL
struct __NSConstantStringImpl {
int *isa;
int flags;
char *str;
long length;
};
#ifdef CF_EXPORT_CONSTANT_STRING
extern "C" __declspec(dllexport) int __CFConstantStringClassReference[];
#else
__OBJC_RW_DLLIMPORT int __CFConstantStringClassReference[];
#endif
#define __NSCONSTANTSTRINGIMPL
#endif
#ifndef BLOCK_IMPL
#define BLOCK_IMPL
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
// Runtime copy/destroy helper functions (from Block_private.h)
#ifdef __OBJC_EXPORT_BLOCKS
extern "C" __declspec(dllexport) void _Block_object_assign(void *, const void *, const int);
extern "C" __declspec(dllexport) void _Block_object_dispose(const void *, const int);
extern "C" __declspec(dllexport) void *_NSConcreteGlobalBlock[32];
extern "C" __declspec(dllexport) void *_NSConcreteStackBlock[32];
#else
__OBJC_RW_DLLIMPORT void _Block_object_assign(void *, const void *, const int);
__OBJC_RW_DLLIMPORT void _Block_object_dispose(const void *, const int);
__OBJC_RW_DLLIMPORT void *_NSConcreteGlobalBlock[32];
__OBJC_RW_DLLIMPORT void *_NSConcreteStackBlock[32];
#endif
#endif
#define __block
#define __weak

#include
struct __NSContainer_literal {
void * *arr;
__NSContainer_literal (unsigned int count, ...) {
va_list marker;
va_start(marker, count);
arr = new void *[count];
for (unsigned i = 0; i < count; i++)
arr[i] = va_arg(marker, void *);
va_end( marker );
};
~__NSContainer_literal() {
delete[] arr;
}
};
extern "C" __declspec(dllimport) void * objc_autoreleasePoolPush(void);
extern "C" __declspec(dllimport) void objc_autoreleasePoolPop(void *);

struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};

#define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)
#include

int main( int argc, const char *argv[] ) {
printf( "hello world\n" );
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

$ g++ -o Hello Hello.cpp
$ ./Hello
hello world

至於如何寫 Clang Rewriter 呢,就是直接參考 lib/Rewrite/RewriteObjC.cpp 這隻幾千行的程式碼吧!裡頭已經有豐富的資料可以參考,此例主要的有兩個 class :


class RewriteObjC : public ASTConsumer
class RewriteObjCFragileABI : public RewriteObjC


程式主體是 RewriteObjC,但使用時是用 RewriteObjCFragileABI:


ASTConsumer *clang::CreateObjCRewriter(const std::string& InFile, raw_ostream* OS, DiagnosticsEngine &Diags, const LangOptions &LOpts, bool SilenceRewriteMacroWarning) {
return new RewriteObjCFragileABI(InFile, OS, Diags, LOpts, SilenceRewriteMacroWarning);
}

一開始的程式進入點在 ASTConsumer constructor,隨後還有 virtual void Initialize(ASTContext &context),接著就跟 clang plugin example PrintFunctionNames 流程差不多,可以追 ASTConsumer 的 public functions 瞧瞧,如 virtual bool HandleTopLevelDecl(DeclGroupRef D) 等。


因此,撰寫的主體大概擺在 bool HandleTopLevelDecl(DeclGroupRef D) 裡頭,此例在 RewriteObjC 的宣告及 HandleTopLevelDecl 定義處:


// Top Level Driver code.
virtual bool HandleTopLevelDecl(DeclGroupRef D) {
for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) {
if (ObjCInterfaceDecl *Class = dyn_cast<ObjCInterfaceDecl>(*I)) {
if (!Class->isThisDeclarationADefinition()) {
RewriteForwardClassDecl(D);
break;
}
}

if (ObjCProtocolDecl *Proto = dyn_cast<ObjCProtocolDecl>(*I)) {
if (!Proto->isThisDeclarationADefinition()) {
RewriteForwardProtocolDecl(D);
break;
}
}

HandleTopLevelSingleDecl(*I);
}
return true;
}

另外一塊就是 void RewriteObjC::HandleTopLevelSingleDecl(Decl *D):


//===----------------------------------------------------------------------===//
// Top Level Driver Code
//===----------------------------------------------------------------------===//

void RewriteObjC::HandleTopLevelSingleDecl(Decl *D) {
//
//...
//

// If we have a decl in the main file, see if we should rewrite it.
if (SM->isFromMainFile(Loc)) // SM = SourceManager ; Loc = SourceLocation
return HandleDeclInMainFile(D);
}

別忘了瞧瞧 HandleDeclInMainFile 做了甚麼事囉!裡頭有豐富的 type checking 以及對應的處理機制,最後,則是文字取代的部分:


void ReplaceText(SourceLocation Start, unsigned OrigLength, StringRef Str) {
// If removal succeeded or warning disabled return with no warning.
if (!Rewrite.ReplaceText(Start, OrigLength, Str) || SilenceRewriteMacroWarning)
return;
Diags.Report(Context->getFullLoc(Start), RewriteFailedDiag);
}

如此一來,對 Clang Rewriter 的架構也熟悉了!


2012年5月24日 星期四

[OSX] 使用開機光碟安裝、重灌 Mac OS X Lion @ Mac mini

01

最近很忙,很久沒開啟 Mac mini 了,順便就把它升到 Lion 了。只是從 Mac Snow Leopard 10.6.8 升到 Lion 後,很多東西都保留了下來,對於大部分的人都很夠用,但我又開始在想,如果想要重灌該怎辦?這安裝方式不像以前購買 Mac mini 附的安裝光碟,可以從桌面上點選幾下就可以光碟開機、刪除硬碟資料並安裝 Mac OS X,所以,我找了一陣子才發現該怎樣光碟開機 :P 偏偏我的鍵盤是 PS/2 轉接 USB 後再加個 KVM,重開機的熱鍵少說也試了十多次吧 :P

關於 Mac OS X 開機熱鍵:Intel 型 Mac 開機組合鍵

實在是光碟開機一直沒成功,就改用 Startup Manager 方式,也就是開機時 + Option 鍵的方式(一般鍵盤是 ALT 鍵),試了幾次比較能抓住訣竅 XD 就是聽到開機聲音後馬上接著按(不要等音效播完),如此一來就能進去設定選單。

04

接著就接近以前 Snow Leopard 安裝光碟一樣,可以先把硬碟處理一下,再接著重裝 Lion 囉!

06

附帶一提,如果從 Startup Manager 進去,並點選 OS X Lion Recovery HD 進去,清完硬碟後,直接點選重新安裝 Lion 時,此時會要求透過網路從 App Store 下載,這時若有 USB 安裝方式的話,可以選擇重新選擇啟動硬碟並選擇安裝來源的媒介,這樣就可以透過 USB 或光碟來安裝了,就不需要重新從網路下載。

[OSX] Mac OS X Lion 軟體清單及偏好設定 @ Mac mini

Mac OS X Lion

前幾天裝了 Ubuntu 12.04 Desktop 版後,發現內鍵的桌布配色真不錯,雖然只是裝在 Virtualbox 裡頭,也令人感到清新。今晚就手癢也把 Mac mini 重灌完啦,神清氣爽。

每次安裝的軟體及設定越來越少了?不知算不算走回 command-line 的生活呢?


  • 文字編輯


    • 偏好設定 -> 預設純文字 && 字型大小 14

    • 保留在 Dock 上

  • 終端機


    • 偏好設定 -> 設定 -> Pro 預設 && 字型大小 14

    • 保留在 Dock 上

  • Yahoo! KeyKey (Yahoo!奇摩輸入法)


    • 系統偏好設定 -> 語言與文字 -> 輸入來源 -> 勾選 Yahoo 奇摩輸入法

  • Skype


    • 偏好 -> 音效影像 -> 麥克風&揚聲器 -> USB PnP Sound Devices (由於我多買一支 USB 音效卡,額外再設定 Skype 播放收音的位置)

    • 偏好 -> 警示 -> 細節 -> 聯絡人可接聽/聯絡人狀態轉為無法接通 -> 取消 播放音效 & 顯示內建的通知訊息

    • 偏好 -> 隱私 -> 取消 "允許 Skype 使用非個人身分資訊作為第三方廣告服務用途"

    • 保留在 Dock 上 & 登入自動啟動

  • Pidgin

  • Filezilla

  • Firefox


    • 工具 -> 附加元件 -> firebug、新同文堂

    • 檢視 -> 工具列 ->顯示書籤列、取消同文堂

    • 偏好 -> 一般 -> 設定首頁

    • 偏好 -> 個人隱私 -> 不被追蹤、不保留歷史紀錄

    • 安裝 Flash player

  • Google Chrome


    • 設定 -> 顯示首頁按鈕、顯示書籤列、設定首頁、取消翻譯閱讀網頁、取消記錄與詢問密碼

    • 擴充功能 -> 新同文堂、XMarks Bookmark Sync、Google 閱讀器通知程式 (由 Google 提供)、Google Mail Checker

2012年5月21日 星期一

Time Station

TimeStation


該怎麼說呢?開始感覺到時間的不夠用了,人生不像大學期中期末考前,一夜不睡,至少可以追到七八成。


Android 開發筆記 - 為自己的 app 建立 Content Provider

MyContentProvider


整個流程在 Android 官網(Create Your Content Provider)已經有很豐富的介紹,在此筆記一下 :P 加快自己往後回憶。


新增自己的 content provider 的用亦是供別人來存取自身 app 的資料(大多是 SQLite 操作),而實做流程並不難,但有一些小事要留意:



  1. 假設你的 app 的 package name 為 com.example.study,實做為 Content Provider Class 為 MyContentProvider。

  2. 首先,建立一個 app 名為 MyContentProvider,但不需要 Activity,新增完後,在新增一個 class 名為 MyContentProvider 並繼承 android.content.ContentProvider。

  3. 由於 content provider 給予外頭存取時,用到的格式為 "content://AUTHORITY/DBTable" 的型態,故決定 Content Provider 的 AUTHORITY 數值及使用的 SQLite db table name,此例定為 "com.example.study" 和 "MyTable1"、"MyTable2" 兩者。

  4. 實做 SQLiteOpenHelper,此例為 MyDBHelper,過程中建立兩張表,分別為 MyTable1 和 MyTable2 兩張表



    • CREATE TABLE MyTable1 ( _ID INTEGER PRIMARY KEY, _DATE DATETIME NULL )
      CREATE TABLE MyTable2 ( _ID INTEGER PRIMARY KEY, _DATA VARCHAR(50) NULL )




  5. 決定 content provider 所提供的 URI 服務後,使用 UriMatcher 來實做判斷方式



    • private static final UriMatcher mUriMatcher;
      private static final int URI_TYPE_TABLE1 = 1;
      private static final int URI_TYPE_TABLE2 = 2;
      static {
              mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
              mUriMatcher.addURI(AUTHORITY, MyDBHelper._DB_TABLE1, URI_TYPE_TABLE1);
              mUriMatcher.addURI(AUTHORITY, MyDBHelper._DB_TABLE2, URI_TYPE_TABLE2);
      }




  6. 實做 content provider 的相關函數,此例僅實做 String getType(Uri uri) 和 Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) 兩個函數,前者用來告知 URI 所回傳的資料型態,後者則是 URI Query 時,將取得的 Cursor 操作方式(此為 SQLite 常用方式)

  7. 設定 AndroidManifest.xml,跟系統註冊使用 content provider。AndroidManifest.xml -> Application 分頁 -> Application Nodes -> Add -> Provider -> Name 欄位瀏覽 -> 選擇 MyContentProvider 後,並輸入 Authorities 欄位資訊(此例為com.example.study)

  8. 最後,實做一個 TestingActivity 來執行 content provider 的 query 動作,並從 Log.w 觀察情況。


程式碼:


MyContentProvider.java:


package com.example.study;


import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;


public class MyContentProvider extends ContentProvider {
        private static final String AUTHORITY = "com.example.study";
        private static final UriMatcher mUriMatcher;
        private static final int URI_TYPE_TABLE1 = 1;
        private static final int URI_TYPE_TABLE2 = 2;
        static {
                mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
                mUriMatcher.addURI(AUTHORITY, MyDBHelper._DB_TABLE1, URI_TYPE_TABLE1);
                mUriMatcher.addURI(AUTHORITY, MyDBHelper._DB_TABLE2, URI_TYPE_TABLE2);
        }
        private class MyDBHelper extends SQLiteOpenHelper {
                public static final String _DB_NAME = "MyDatabases.db";
                public static final String _DB_TABLE1 = "MyTable1";
                public static final String _DB_TABLE2 = "MyTable2";
                public static final int _DB_VERSION = 1;


                public MyDBHelper(Context context) {
                        super(context, _DB_NAME, null, _DB_VERSION);
                }


                public MyDBHelper(Context context, String name, CursorFactory factory, int version) {
                        super(context, name, factory, version);
                }


                @Override
                public void onCreate(SQLiteDatabase db) {
                        db.execSQL( "CREATE TABLE "+_DB_TABLE1+" ( _ID INTEGER PRIMARY KEY, _DATE DATETIME NULL )" );
                        db.execSQL( "CREATE TABLE "+_DB_TABLE2+" ( _ID INTEGER PRIMARY KEY, _DATA VARCHAR(50) NULL )" );
                }


                @Override
                public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
                        db.execSQL("DROP TABLE IF EXISTS "+_DB_TABLE1);
                        db.execSQL("DROP TABLE IF EXISTS "+_DB_TABLE2);
                }
        }
        MyDBHelper mHelper = null;


        private void genTesting() {
                SQLiteDatabase mDB = mHelper.getWritableDatabase();

                // MyDBHelper._DB_TABLE1
                mDB.execSQL("INSERT INTO "+MyDBHelper._DB_TABLE1+" (_DATE) VALUES (datetime('now'))");


                // MyDBHelper._DB_TABLE2
                ContentValues values = new ContentValues();
                values.put("_DATA", "Hello World!");
                mDB.insertOrThrow(MyDBHelper._DB_TABLE2, null, values);


                mDB.close();
        }

        @Override
        public boolean onCreate() {
                // TODO Auto-generated method stub
                mHelper = new MyDBHelper(getContext());
                return true;
        }


        @Override
        public String getType(Uri uri) {
                // TODO Auto-generated method stub
                switch(mUriMatcher.match(uri)) {
                        case URI_TYPE_TABLE1:
                                return "vnd.android.cursor.item/vnd."+AUTHORITY+"."+MyDBHelper._DB_TABLE1; // one row
                        case URI_TYPE_TABLE2:
                                return "vnd.android.cursor.dir/vnd."+AUTHORITY+"."+MyDBHelper._DB_TABLE2; // multiple rows
                        default:
                                throw new IllegalArgumentException("Unknown URI " + uri);
                }
        }


        @Override
        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
                // TODO Auto-generated method stub
                genTesting();
                Cursor out = null;
                SQLiteDatabase mDB = null;
                switch(mUriMatcher.match(uri)) {
                        case URI_TYPE_TABLE1:
                                mDB = mHelper.getWritableDatabase();
                                out = mDB.rawQuery("SELECT * FROM "+MyDBHelper._DB_TABLE1+" ORDER BY _ID DESC LIMIT 1", null);
                                out.moveToFirst();
                                break;
                        case URI_TYPE_TABLE2:
                                mDB = mHelper.getWritableDatabase();
                                out = mDB.rawQuery("SELECT * FROM "+MyDBHelper._DB_TABLE2, null);
                                out.moveToFirst();
                                break;
                        default:
                                throw new IllegalArgumentException("Unknown URI " + uri);
                }
                return out;
        }


        @Override
        public Uri insert(Uri uri, ContentValues values) {
                // TODO Auto-generated method stub
                return null;
        }


        @Override
        public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
                // TODO Auto-generated method stub
                return 0;
        }

        @Override
        public int delete(Uri arg0, String arg1, String[] arg2) {
                // TODO Auto-generated method stub
                return 0;
        }
}


MyContentProviderTestingActivity.java:


package com.example.study.test;


import android.app.Activity;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;


public class MyContentProviderTestingActivity extends Activity {
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.main);

                Cursor c = getContentResolver().query(
                        Uri.parse("content://com.example.study/MyTable1"),
                        null,null,null,null ); // get all
                if( c != null ) {
                        c.moveToFirst();
                        while(c.isAfterLast()==false) {
                                Log.e("CP","MyTalbe1 field1:"+c.getString(0)+", field2:"+c.getString(1));
                                c.moveToNext();
                        }
                        c.close();

                        c = getContentResolver().query(
                                Uri.parse("content://com.example.study/MyTable2"),
                                null,null,null,null ); // get all
                        c.moveToFirst();
                        while(c.isAfterLast()==false) {
                                Log.e("CP","MyTalbe2 field1:"+c.getString(0)+", field2:"+c.getString(1));
                                c.moveToNext();
                        }
                        c.close();

                }
        }
}