FastCGI のプロセスを strace する
メモしておきます。
1. プロセスマネージャに以下のように attach する(-ff は fork したプロセスにも自動で attach してくれる)
$ sudo strace -ff -o fastcgi_strace_log -p <fcgi プロセスマネージャの PID>
2. fcgi サーバーのプロセスを殺す
$ sudo pkill application.fcgi
3. すると、プロセスマネージャが新しい fcgi サーバーのプロセスを立ち上げるので、そのプロセスに strace で attach できる
$ ls fastcgi_strace_log.<fcgi サーバーの PID> fastcgi_strace_log.<fcgi サーバーの PID> fastcgi_strace_log.<fcgi サーバーの PID> fastcgi_strace_log.<fcgi サーバーの PID> fastcgi_strace_log.<fcgi サーバーの PID> fastcgi_strace_log.<fcgi サーバーの PID>
私的 Win32 API メモ
いろいろ勉強した
忘れてしまいそうなので書き下しておく
雰囲気的なこと
- ウィンドウ
- ハンドル
- ポインタみたいなもん
- HWND
- ウィンドウのハンドル
- DOM でいうと Node みたいなもん
- LPXXX
- XXX のポインタって意味
- owner と popup
- 「ポップアップさせたウィンドウ = owner」と「ポップアップしたウィンドウ = popup」
- DOM でいうと
- owner は、 window.opener で
- popup は、 window.frames って感じ
- HINSTANCE
- DLL とか EXE とかひとつと対応してるハンドル
- アプリケーションの HINSTANCE というと実行されている EXE のハンドル
- DLL や EXE のメモリ上の位置
- HMODULE とは同じもの
あまり本質的でないこと
- W で終わる関数は WCHAR
- WideCharToMultiByte を使えば、 WCHAR -> char
int len = WideCharToMultiByte(CP_UTF8, 0, wbuf, wlen, buf, sizeof(buf) - 1, NULL, NULL);
ウィンドウの取得
- HWND GetWindow(HWND, 定数)
- 様々な方向に、要素を取得しにいく
- GetWindow(HWND, GW_CHILD)
- DOM でいうと node.firstChild
- GetWindow(HWND, GW_HWNDNEXT)
- DOM でいうと node.nextSibling
- GetWindow(HWND, GW_HWNDPREV)
- DOM でいうと node.previousSibling
- GetWindow(HWND, GW_HWNDFIRST)
- DOM でいうと node.parentNode.firstChild
- GetWindow(HWND, GW_HWNDLAST)
- DOM でいうと node.parentNode.lastChild
- GetWindow(HWND, GW_OWNER)
- DOM でいうと window.opener
- GetWindow(HWND, GW_ENABLEDPOPUP)
- DOM でいうと window.frames[0]
- HWND GetParent(HWND)
- DOM でいうと node.parentNode
- AnyPopup
- ポップアップウィンドウを持ってるか
- DOM でいうと (window.frame.length != 0)
- BOOL EnumChildWindows(HWND, WNDENUMPROC, LPARAM)
- DOM でいうと node.getElementsByTagName('*').forEach(function() { }); みたいなもん
- 第三引数は何でも OK (ポインタとか入れとけば OK)
- あっちこっち何回もトラバースするときは、これ使えってことらしい
- HWND GetDesktopWindow()
- デスクトップのウィンドハンドルを返す
- DOM でいうと document.documentElement とか document.body
WHND から取得できる情報
- Class
- ClassName
- GetClassName(hwnd, buf, len)
- WNDCLAASSEX
- GetClassInfo(hinstance, name, &info) で取れる
- hinstance は GetWindowLong で
- WNDCLAASSEX は情報満載
- http://homepage1.nifty.com/kan01/program/vc/win_info.htm
- lpfnWndProc で、メッセージ処理
- ClassName
- HINSTANCE
- GetWindowLong(hwnd, GWL_HINSTANCE)
- CreateWindow, CreateWindowEx したときに渡したやつ
- スタイル
- GetWindowStyle(HWND) & flags, GetWindowExStyle(HWND) & flags
- LVS_ とか TBSTYLE_ とか CCS_ とか WS_ とか Class Name によっていろいろ
- タイトル的な何か
- GetWindowText(hwnd, buf, sizeof(buf) - 1)
- 矩形
- GetWindowRect(hWnd,&rect)
- ウィンドウを作成したプロセス ID とスレッド ID
- threadid = GetWindowThreadProcessId(HWND, &processid)
- プロセス ID から HINSTANCE を得る
- OpenProcess 使う
- SendMessage
- なんでもあり、奥義
- SendMessage で情報を取得する場合で、外部プロセスのウィンドウの場合は VirtualAllocEx で
- VirtualAllocEx
- 外部プロセスのメモリを確保
- HWND → GetWindowThreadProcessId → OpenProcess → VirtualAllocEx の手順
- ReadProcessMemory で読む
- VirtualFreeEx で開放
ブラウザで X86 のマシン語を動かす! Google 謹製 Native Client をさっそく試してみる
はじめに
Google から、非常に面白そうなソフトウェアがリリースされました!
その名も Native Client なんとブラウザ上で X86 のバイナリを動かしてしまうそうです。
これはすごい!
さっそく試してみたいと思います。その過程を逐次更新していきます。
自分が試したときの環境
自分が試す環境は、以下の通りです。
準備
では、さっそく準備をしましょう。
http://nativeclient.googlecode.com/svn/trunk/nacl/googleclient/native_client/documentation/getting_started.html を参考にやってみます。
環境一式をダウンロード
まずは、以下から nacl_mac_0.1_9308700.tgz をダウンロードしてくきます。
http://code.google.com/p/nativeclient/downloads/list
$ wget http://nativeclient.googlecode.com/files/nacl_mac_0.1_9308700.tgz --2008-12-09 15:10:07-- http://nativeclient.googlecode.com/files/nacl_mac_0.1_9308700.tgz nativeclient.googlecode.com をDNSに問いあわせています... 74.125.47.82 nativeclient.googlecode.com|74.125.47.82|:80 に接続しています... 接続しました。 HTTP による接続要求を送信しました、応答を待っています... 200 OK 長さ: 83237741 (79M) [application/x-gzip] `nacl_mac_0.1_9308700.tgz' に保存中 100%[==================================================================================================================>] 83,237,741 2.61M/s 時間 36s 2008-12-09 15:10:44 (2.21 MB/s) - `nacl_mac_0.1_9308700.tgz' へ保存完了 [83237741/83237741]
解凍
tgz だったので、 tar で解凍します。
$ tar xvfz nacl_mac_0.1_9308700.tgz
中を見てみる
tar すると nacl というディレクトリにいろいろと解凍されました。
解凍されたディレクトリを tree してみました。
ng$ cd .. $ tree -dL 3 nacl nacl `-- googleclient |-- native_client | |-- common | |-- documentation | |-- gtest | |-- include | |-- intermodule_comm | |-- ncv | |-- nonnacl_util | |-- npapi_plugin | |-- scons-out | |-- service_runtime | |-- site_scons | |-- site_scons_general | |-- tests | |-- third_party | |-- tools | `-- tools_bin `-- third_party |-- binutils |-- gcc |-- gnu_binutils |-- gtest |-- libxt |-- newlib |-- npapi |-- scons `-- sdl 28 directories
サンプルを実行してみる
サンプルは、 nacl/googleclient/native_client/tests/ の中に入っているみたいです。
tree -dL 1 nacl/googleclient/native_client/tests/ nacl/googleclient/native_client/tests/ |-- Frameworks |-- app_lib |-- cloudfs |-- createthreads |-- earth |-- eviltests |-- fib |-- file |-- hello_world |-- imc_shm_mmap |-- life |-- mandel |-- mandel_nav |-- mm_init |-- mmap |-- noop |-- npapi_bridge |-- npapi_hw |-- npapi_pi |-- nrd_xfer |-- null |-- plug_univ |-- quake |-- srpc |-- srpc_hw |-- syscalls |-- tone |-- voronoi `-- xaos 29 directories
なんか、いろいろあります。
では、 life というサンプルを実行してみましょう!
$ cd nacl/googleclient/native_client/tests/ $ python run.py
おおお。なんか、ライフゲームが立ち上がりました!
ディレクトリの中身を見てみましょう。
$ ls -la total 64 drwxr-xr-x 2 amachang staff 272 12 8 12:45 . drwxr-xr-x 31 amachang staff 1054 12 8 12:45 .. -r--r--r-- 1 amachang staff 1882 12 8 12:45 Makefile -r--r--r-- 1 amachang staff 166 12 8 12:45 README.txt -r--r--r-- 1 amachang staff 1874 12 8 12:45 SConscript.nacl -r-xr-xr-x 1 amachang staff 9795 12 8 12:45 life.cc -r--r--r-- 2 amachang staff 1134 12 8 12:45 life.html -r-xr-xr-x 1 amachang staff 1786 12 8 12:45 run.py
ビルド用の Makefile と SConscript と、ソースコード life.cc と、 html に貼付けるための life.html、単品実行用の run.py って感じですね。
ちょっと脱線:ソースコードを覗き見してみる
ソースコードをちらっと見てみましょう。
include を見ると、以下のように nacl/nacl_* というファイルを include しています。
standalone で実行する時は、違うヘッダを読み込むようですね。#if !defined(STANDALONE) #include <nacl/nacl_av.h> #include <nacl/nacl_srpc.h> #else #include "native_client/common/standalone.h" #endif描画は、以下のように nacl_video_update に uint32_t のバッファを渡してやるようです。
struct Surface { int width, height, pitch; uint32_t *pixels; Surface(int w, int h) { width = w; height = h; pitch = w; pixels = new uint32_t[width * height]; } ~Surface() { delete[] pixels; } }; : : // Copies sw rendered life image to screen void Life::Draw() { int r; r = nacl_video_update(surf_->pixels); if (-1 == r) { printf("nacl_video_update() returned %d\n", errno); } }詳しいことはこの時点ではよく分かりません。
でも、このアプリケーションは 341 行という短いコードで動いているようです。
ちょっと脱線: run.py は何をやっているの?
追いかけてみたら、 run.py は以下のコマンドを実行しているみたいです。
$ nacl/googleclient/native_client/scons-out/dbg-mac/staging/sel_ldr -d -f nacl/googleclient/native_client/scons-out/nacl/staging/life.nexesel_ldr というプログラムは、スタンドアローン版の flash player のようなもので nexe というが swf のようなものなのでしょう。
- -f オプションはファイル指定
- -d オプションはデバッグ
という意味だそうです。
life.nexe というのはどういうファイルなのか?
ちょっと気になるので、 file してみました
$ file scons-out/nacl/staging/life.nexe scons-out/nacl/staging/life.nexe: ELF 32-bit LSB executable, Intel 80386, version 1, statically linked, not strippedどうやら、 *.nexe は ELF 32-bit LSB バイナリ(リナックスの実行ファイルの形式)だということが分かりました。
プラグインをビルドする
SCons でビルド出来るようです。
$ cd googleclient/native_client $ scons --help scons: Reading SConscript files ... EnvironmentError: No module named component_setup: File "/Users/amachang/nc/nacl/googleclient/native_client/SConstruct", line 52: COMPONENT_LIBRARY_PUBLISH = True, File "/opt/local/lib/scons-0.98.4/SCons/Environment.py", line 933: apply_tools(self, tools, toolpath) File "/opt/local/lib/scons-0.98.4/SCons/Environment.py", line 106: env.Tool(tool) File "/opt/local/lib/scons-0.98.4/SCons/Environment.py", line 1582: tool = apply(SCons.Tool.Tool, (tool, toolpath), kw) File "/opt/local/lib/scons-0.98.4/SCons/Tool/__init__.py", line 89: module = self._tool_module() File "/opt/local/lib/scons-0.98.4/SCons/Tool/__init__.py", line 140: raise SCons.Errors.EnvironmentError, e
怒られてしまいました><
どうやら、 Native Client が用意した scons を使えということらしいですね。
$ ./scons --help scons: Reading SConscript files ... ====================================================================== Building nexe binaries using sdk at [/Users/amachang/nc/nacl/googleclient/native_client/tools_bin/mac/sdk/nacl-sdk] ====================================================================== ====================================================================== SDL build enabled, this is somewhat experimental Using version in /Users/amachang/nc/nacl/googleclient/native_client/../third_party/sdl/osx/v1_2_13 ====================================================================== *** Solution file generation skipped (not supported on this platform). scons: done reading SConscript files. Additional options for SCons: --mode=MODE Specify build mode (see below). --host-platform=PLATFORM Force SCons to use PLATFORM as the host platform, instead of the actual platform on which SCons is run. Useful for examining the dependency tree which would be created, but not useful for actually running the build because it'll attempt to use the wrong tools for your actual platform. --site-path=DIRLIST Comma-separated list of additional site directory paths; each is processed as if passed to --site-dir. --verbose Print verbose output while building, including the full command lines for all commands. --brief Print brief output while building (the default). This and --verbose are opposites. Use --silent to turn off all output. --retest Rerun specified tests, ignoring cached results. ====================================================================== Help for NaCl ====================================================================== Common tasks: ------------- * cleaning: scons -c * build mandel: scons MODE=all mandel.nexe * some unittests: scons run_unit_tests * a smoke test: scons -k pp=1 smoke_test * 2nd smoke test: scons -k pp=1 MODE=nacl smoke_test * documentation: scons MODE=doc * firefox plugin: scons MODE=opt-linux npGoogleNaClPlugin * sel_ldr: scons MODE=opt-linux sel_ldr * firefox install: scons firefox_install Options: -------- pp=1 use command line pretty printing (more concise output) sdl=<mode> where <mode>: 'none': don't use SDL (default) 'local': use locally installed SDL 'hermetic': use the hermetic SDL copy naclsdk_mode=<mode> where <mode>: 'local': use locally installed sdk kit 'download': use the download copy (default) 'custom:<path>': use kit at <path> Automagically generated help: ----------------------------- Use --mode=type to specify the type of build to perform. The following types may be specified: dbg-mac MacOS debug build opt-mac MacOS optimized build nacl NaCl module build doc Documentation build The following build groups may also be specified via --mode. Build groups build one or more of the other build types. The available build groups are: all dbg-mac,opt-mac,nacl,doc default dbg-mac Multiple modes may be specified, separated by commas: --mode=mode1,mode2. If no mode is specified, the default group will be built. This is equivalent to specifying --mode=default. The following libraries can be built: gio naclthread nonnacl_util_c google_nacl_imc ncvalidate nrd_xfer google_nacl_imc_c ncvtest sel google_nacl_npruntime nonnacl_srpc gtest nonnacl_util all_libraries (do all of the above) The following large tests can be run: run_service_runtime_tests simple_tests run_large_tests (do all of the above) The following tests can be run: run_service_runtime_tests simple_tests run_all_tests (do all of the above) The following bundles can be built: /Users/amachang/nc/nacl/googleclient/native_client/scons-out/dbg-mac/staging/npGoogleNaClPlugin.bundle all_bundles (do all of the above) The following programs can be built: client ncval sel_universal nacl_cpuid npGoogleNaClPlugin server nacl_ldt_unittest npapi_test service_runtime_tests ncdecode_table nrd_xfer_test ncdis sel_ldr all_programs (do all of the above) Use scons -H for help about command-line options.
おおお。このツールでなんでも作れるみたいですね。
さっそく Firefox のプラグインをインストールします。
$ ./scons --prebuilt firefox_install scons: Reading SConscript files ... ====================================================================== Building nexe binaries using sdk at [/Users/amachang/nc/nacl/googleclient/native_client/tools_bin/mac/sdk/nacl-sdk] ====================================================================== *** Solution file generation skipped (not supported on this platform). scons: done reading SConscript files. scons: Building targets ... /System/Library/Frameworks/Python.framework/Versions/2.5/Resources/Python.app/Contents/MacOS/Python ./tools/firefoxinstall.py MODE=0 PLATFORM_BASE="/Users/amachang/nc/nacl/googleclient/native_client/scons-out/" MODE=0 PLATFORM_BASE=/Users/amachang/nc/nacl/googleclient/native_client/scons-out/ This script will install: /Users/amachang/nc/nacl/googleclient/native_client/scons-out/opt-mac/staging/npGoogleNaClPlugin.bundle in /Users/amachang/Library/Internet Plug-Ins/npGoogleNaClPlugin.bundle /Users/amachang/nc/nacl/googleclient/native_client/scons-out/opt-mac/staging/sel_ldr in /Users/amachang/Library/Internet Plug-Ins/npGoogleNaClPlugin.bundle/Contents/Resources and /Users/amachang/nc/nacl/googleclient/native_client/scons-out/opt-mac/Frameworks/SDL.framework in /Users/amachang/Library/Frameworks/SDL.framework Okey to continue? [y/n]
sel_ldr や SDL.framework や npGoogleNaClPlugin.bundle がインストールされる見たいですね。
良ければ、 y と答えます。
Okey to continue? [y/n] y Okay, you asked for it. copying directory /Users/amachang/nc/nacl/googleclient/native_client/scons-out/opt-mac/staging/npGoogleNaClPlugin.bundle to /Users/amachang/Library/Internet Plug-Ins/npGoogleNaClPlugin.bundle ... copying directory /Users/amachang/nc/nacl/googleclient/native_client/scons-out/opt-mac/Frameworks/SDL.framework to /Users/amachang/Library/Frameworks/SDL.framework ... copying /Users/amachang/nc/nacl/googleclient/native_client/scons-out/opt-mac/staging/sel_ldr to /Users/amachang/Library/Internet Plug-Ins/npGoogleNaClPlugin.bundle/Contents/Resources ... ********************************************************************* * You have successfully installed the NaCl Firefox plugin. * As a self-test, please confirm you can run * /Users/amachang/Library/Internet Plug-Ins/npGoogleNaClPlugin.bundle/Contents/Resources/sel_ldr * from a shell/command prompt. With no args you should see * No nacl file specified * on Linux or Mac and no output on Windows. ********************************************************************* * To test this installation also try the test links on the page * scons-out/nacl/staging/index.html ********************************************************************* scons: done building targets.
おおお。成功したみたいです。
ブラウザでサンプルを見てみる。
さっそく、 Firefox を起動して、 scons-out/nacl/staging/earth.html を見てみましょう。
おおおおおお。めっちゃ軽いです!
自分で動くものを作ってみる
では、さっそく何か作ってみましょう。
足し算するだけの nexe を作ってみる
まずは、以下のような add.c というファイルを作ります。
#include <nacl/nacl_srpc.h> int Add(NaClAppArg **in_args, NaClAppArg **out_args) { out_args[0]->u.ival = in_args[0]->u.ival + in_args[1]->u.ival; return RPC_OK; } NACL_SRPC_METHOD("add:ii:i", Add);
次に、以下のような add.html を作ります。
<!DOCTYPE html> <html> <head> <title>test</title> </head> <body> <embed id="nacl" type="application/x-nacl-srpc" width="0" height="0" src="add.nexe" /> <a href="javascript:void(0);" onclick="alert(document.getElementById('nacl').add(1, 2))">1 + 2 = ?</a> </body> </html>
で、以下のようにコンパイルします。
長い人は PATH を通してしまいましょう。
$ nacl/googleclient/native_client/tools_bin/mac/sdk/nacl-sdk/bin/nacl-gcc -lsrpc -lgoogle_nacl_imc -lpthread -static add.c -o add.nexe
文字列の diff を取る nexe を作ってみる
http://labs.unoh.net/2008/11/diff_with_c.html で紹介されている Google Code Archive - Long-term storage for Google Code Project Hosting. という diff ライブラリを使います。
まず、このライブラリの dtl.hpp を同じディレクトリに置きます。
次に、以下のような C++ のコードを書き diff.cpp とします。
#include "dtl.hpp" #include <iostream> #include <vector> #include <sstream> #include <nacl/nacl_srpc.h> std::string diff(std::string as, std::string bs) { using namespace std; using namespace dtl; // 文字列を行にバラす istringstream ais(as); istringstream bis(bs); string buf; vector<string> av, bv; while(getline(ais, buf)) av.push_back(buf); while(getline(bis, buf)) bv.push_back(buf); // Diff を取る Diff<string, vector<string> > d(av, bv); d.compose(); Ses<string> ses = d.getSes(); vector< pair<string, elemInfo> > v = ses.getSequence(); // 結果を ostringstream に HTML 形式で出力する ostringstream os; vector< pair<string, elemInfo> >::iterator it; for (it = v.begin(); it != v.end(); ++it) { switch (it->second.type) { case SES_ADD : os << "<li class=\"add\">" << it->first << "</li>\n"; break; case SES_DELETE : os << "<li class=\"delete\">" << it->first << "</li>\n"; break; case SES_COMMON : os << "<li class=\"common\">" << it->first << "</li>\n"; break; default : break; } } return os.str(); } // SRPC で呼び出される関数 int Diff(NaClAppArg **in_args, NaClAppArg **out_args) { // diff 関数を呼び出す out_args[0]->u.sval = strdup(diff(in_args[0]->u.sval, in_args[1]->u.sval).c_str()); return RPC_OK; } // diff:ss:s は、文字列を2つ受け取って文字列を返すという意味 NACL_SRPC_METHOD("diff:ss:s", Diff);
そして、さきほどと同じようにコンパイルします。
$ nacl/googleclient/native_client/tools_bin/mac/sdk/nacl-sdk/bin/nacl-g++ -lsrpc -lgoogle_nacl_imc -lpthread -static diff.cpp -o diff.nexe
今回は C++ なので nacl-g++ を使っているところに注意してください。
diff.nexe が出来たので、以下のような diff.html を作ります。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Diff Sample</title> <style type="text/css"> html { width: 100%; margin: 0; padding 0; } body { width: 96%; margin: 2%; padding 0; } body > div { float: left; width: 50%; } body > div > textarea { width: 90%; height: 10em; } ul { list-style-type: none; margin: 0; padding: 0 } li { white-space: pre } li.add { color: #080; background: #8f8; } li.delete { color: #800; background: #f88; } </style> <script type="text/javascript"> var result, nacl, text1, text2; window.onload = function() { result = document.getElementById('result'); nacl = document.getElementById('nacl'); text1 = document.getElementById('text1'); text2 = document.getElementById('text2'); }; </script> </head> <body> <h1>Diff Sample</h1> <div> <h2>Text 1</h2> <textarea id="text1"> function fib(n){ if(n == 1 || n == 2){ return 1; }else{ return fib(n - 1) + fib(n - 2); } } </textarea> </div> <div> <h2>Text 2</h2> <textarea id="text2"> function fibonatti(n){ if(n == 1 || n == 2){ return 1; }else{ return fibonatti(n - 1) + fibonatti(n - 2); } } </textarea> </div> <p> <a href="javascript:void(0);" onclick="result.innerHTML = nacl.diff(text1.value, text2.value);">diff!</a> </p> <ul id="result"></ul> <embed id="nacl" type="application/x-nacl-srpc" width="0" height="0" src="diff.nexe" /> </body> </html>
自分で Native Client の実行環境をビルドしてみる
いったん clean して
$ ./scons --mode=dbg-mac,nacl - c
デバッグビルドします。
$ ./scons --mode=dbg-mac,nacl
普通に成功しました。
で、 firefox にデバッグ版をインストールします。
$ ./scons firefox_install DBG=1
でも、どうやったらデバッグできるんだろう orz
ドキュメントには、以下のようにインクルードして、普通のローカルアプリケーションとしてビルドすれば、 gdb でも kdbg でもなんでも出来るよね!というようなことが書いてありました。
#if !defined(STANDALONE) #include <nacl/nacl_srpc.h> #else #include "native_client/common/standalone.h" #endif
どうやって nexe は実行されるか
ちょっと調べてみます。
sel_ldr が別プロセスとして立ち上がってる
$ ps auwx | grep sel_ldr amachang 17617 0.0 0.3 1139488 6256 ?? S 8:50PM 0:00.12 /Users/amachang/Library/Internet Plug-Ins/npGoogleNaClPlugin.bundle/Contents/Resources/sel_ldr -f /Users/amachang/nc/sample/diff/diff.nexe -i 5:42 -P 5 -X 5
ページをロードするごとに、一つ sel_ldr プロセスが立ち上がるみたいです。
これと Firefox がどうにか通信しているんですね。
どうやって、 nexe にジャンプするか
service_runtime/nacl_switch.S というアセンブラがあって、以下のようにジャンプしています。
.text .globl IDENTIFIER(NaClSwitch) IDENTIFIER(NaClSwitch): popl %eax /* throw away the return addr */ /* do not leak info to app */ xorl %ecx, %ecx /* xorl will leave eflags in a known state, so no info leaks */ popl %edx /* new eip */ popl %ebp popl %edi popl %esi popl %ebx popl %gs popl %fs popl %es ljmp *(%esp)
飛んだ先はどうなっているか
diff.cpp には main がありませんでした。どこに飛ぶのでしょうか。
libsrpc.a に main が weak シンボルで定義されていました。
int __attribute__ ((weak)) main(int argc, char* argv[]) { /* * Print the methods that are available. __NaClPrintRpcMethods(); */ srpc_init(); /* * Message processing loop goes here. For now, just do a sel_universal. */ __CommandLoop(); return 0; }
では __CommandLoop では何をやっているのでしょうか。
__CommandLoop では何をやっているのか
__CommandLoop は以下のようになっていました。
NaClSrpcError __CommandLoop() { : : socket_desc = __srpc_get_fd(); if (socket_desc == -1) { /* * No socket connection, use stdin/stdout. */ rpc_desc = __BuildInterfaceDescription(&num_rpc); // process commands from stdin and dispatch for (;;) { : : } } else { NaClSrpcChannel channel; : : for (;;) { : : } : : } return RPC_OK; }
コメントを見る限りは、ソケット通信しているかどうかでループを分けています。
SRPC メッセージのフォーマットは?
tools/libsrpc/rpc_serialize.c のコメントのところに書いてあります。
SRPC の解析処理 → 関数実行の流れ
Firefox からデータが送られてくると tools/libsrpc/rpc_serialize.c の以下の箇所で rpc_number が抽出され
: retval = ImcRead(&client_protocol, sizeof(client_protocol), 1, channel); : retval = ImcRead(&rpc_number, sizeof(rpc_number), 1, channel); :
以下の箇所で、実際にアプリケーションに書いたあれらの関数(さっきの例の Add や Diff)が呼び出されます。
app_error = (channel->rpc_descr[rpc_number].handler)(args, rets);
また、ページ遷移すると Firefox から NACL_SRPC_SHUTDOWN_METHOD というメッセージが送られてきて
以下の SRPC のメソッドが呼ばれ、 nexe は終了します。
static int srpc_shutdown_request(NaClAppArg **in_args, NaClAppArg **out_arg) { if (srpc_privileged) { _exit(0); } return RPC_OK; } NACL_SRPC_METHOD("shutdown::", srpc_shutdown_request);
このメッセージ(NACL_SRPC_SHUTDOWN_METHOD)は、 JavaScript からも element.shutdown() というように呼び出すことで送出することが出来ます。
まとめ
そろそろまとめますよっと。とりあえず試してみた Native Client ですが、ちょっと難しかったです><
でも、ネイティブのコードが動くというのはやっぱり凄いですね。
Alchemy のようなアプローチでは(C → LLVM → AS → ABC という感じ)、やはり速度には限界がありますし。
ただ、セキュリティをどうやって確保しているのかが気になりますね。もちろん、ウェブページに埋め込むようなものなのできちんとしたサンドボックスが必要ですよね。時間があるときに、そこら辺も調べてみたいですね。
もし、セキュリティ上問題がないのなら、これ以上最強なものはないのではないでしょうか。
追記:id:moira さんがそこら辺のことに言及されています
http://blog.deadbeaf.org/2008/12/09/google-native-client/
さすがです><
質問した
複数の C++ のソースファイルに対して、一度にシンボルの置換を… - 人力検索はてな
もし良かったら、教えてください><
C/C++ のコードを Flash Player で動かす! Alchemy を速攻試してみる。
はじめに
Adobe から C/C++ で書いたコードを Flash や AIR で動かす Alchemy というものがリリースされましたね!
Alchemy - Adobe Labs
これはすごい!ということで、少し試してみたいと思います。
その様子をリアルタイムに書いていきます。ちゃんと出来るかな
環境
OS は Mac OS X で gcc 、 java は入っているものとします。
ホームディレクトリ(/Users/amachang)に AlchemyTest というディレクトリを作って作業します。
インストール
まずは、いろいろインストールします。
Flash Player 10 Debugger Version
Adobe Flash Player - Debug Downloads
ここからダウンロードしてきて、普通にインストールします。
Flex SDK
以下から Flex SDK をダウンロードします。
Flex SDK
現段階での最新の Stable のやつをダウンロードしました。具体的には以下のバージョンのものです。
http://opensource.adobe.com/wiki/display/flexsdk/download?build=3.2.0.3794&pkgtype=1
ダウンロードできたら、解凍します。今回は FlexSDK という名前のディレクトリを作ってそこに解凍しました。
$ unzip flex_sdk_3.2.0.3794.zip -d FlexSDK
とりあえず、 bin に PATH を通しておきましょう。
$ export PATH=/Users/amachang/AlchemyTest/FlexSDK/bin:$PATH
試しに、なんか適当な ActionScript のファイルから swf ファイルを作ってみましょう。
以下のように、適当なディレクトリを作って
$ mkdir Src $ cd Src
そこに、以下のようなファイル Hoge.as を置きます。
package { import flash.display.Sprite public class Hoge extends Sprite { public function Hoge() { } } }
で、さっき FlexSDK に解凍したツールを使ってコンパイルします。
$ mxmlc Hoge.as 設定ファイル "/Users/amachang/AlchemyTest/FlexSDK/frameworks/flex-config.xml" をロードしています /Users/amachang/AlchemyTest/Src/Hoge.swf (555 bytes)
おおお。 swf ファイルがちゃんとできましたね。
というわけで、次は Alchemy Toolkit Package を導入します。
ここまでのディレクトリ構成は以下のような感じです。
$ pwd /Users/amachang/AlchemyTest $ tree -L 2 . |-- FlexSDK | |-- AIR SDK Readme.txt | |-- SDK license.pdf | |-- ant | |-- asdoc | |-- bin | |-- flex-sdk-description.xml | |-- frameworks | |-- lib | |-- license-adobesdk.htm | |-- license-adobesdk_ja.htm | |-- license-mpl.htm | |-- readme.htm | |-- readme_ja.htm | |-- runtimes | |-- samples | `-- templates |-- Src | |-- Hoge.as | `-- Hoge.swf `-- flex_sdk_3.2.0.3794.zip 10 directories, 11 files
Alchemy Toolkit Package
次は Alchemy Toolkit Package をダウンロードしてきます。
Alchemy Toolkit Package
ダウンロードしたら、作業ディレクトリに置いて AlchemyToolkit というディレクトリに解凍します。
$ unzip alchemy_sdk_darwin_p1_111708.zip -d AlchemyToolkit
少し、ディレクトリ構成を見てみましょう。
$ tree -L 2 AlchemyToolkit AlchemyToolkit `-- alchemy-darwin-v0.4a |-- CHANGES |-- LICENSE |-- README.cygwin |-- README.darwin |-- README.ubuntu |-- achacks |-- avm2-libc |-- bin |-- config |-- flashlibs `-- samples 7 directories, 5 files
AlchemyToolkit のトップはこんな感じのディレクトリ構成になっています。
まず、 avm2-libc というのがありますね。この中にが libc.so の代わりにリンクされるライブラリが入っているのでしょうか。
また、 bin にツールが入っているのでしょうか。README を読む前に bin の中身を確認してみましょう。
$ ls AlchemyToolkit/alchemy-darwin-v0.4a/bin/ ExplSWF.pl ShrSWF.pl llvm-as llvm-prof GetABC2.pl V10SWF.pl llvm-bcanalyzer llvm-ranlib ImplSWF.pl alc-util llvm-config llvm-stub LICENSE-LLVM.TXT asc.jar llvm-db llvm-upgrade LICENSE-LLVMC.TXT gccas llvm-dis llvm2cpp LICENSE-ZLIB.TXT gccld llvm-extract llvmc PutABC2.pl gluegen llvm-gcc4-darwin-install opt PutBIN.pl llc llvm-ld swfbridge README-LLVM lli llvm-link zpipe README-ZLIB llvm-ar llvm-nm zpipe.pl
おおお LLVM のツールがいっぱい入っています。
というわけで avm2-libc の中をみてみます。
$ ls -la AlchemyToolkit/alchemy-darwin-v0.4a/avm2-libc/ total 0 drwxr-xr-x 5 amachang staff 170 11 14 03:09 . drwxr-xr-x 8 amachang staff 442 11 14 03:09 .. drwxr-xr-x 2 amachang staff 102 11 14 03:09 avm2 drwxr-xr-x 10 amachang staff 2822 11 14 03:09 include drwxr-xr-x 2 amachang staff 136 11 18 20:19 lib
そして、この lib の中に
$ ls -la AlchemyToolkit/alchemy-darwin-v0.4a/avm2-libc/lib/ total 7544 drwxr-xr-x 2 amachang staff 136 11 18 20:19 . drwxr-xr-x 5 amachang staff 170 11 14 03:09 .. -rw-r--r-- 1 amachang staff 1564524 11 14 03:09 avm2-libc.l.bc -rw-r--r-- 1 amachang staff 2297544 11 14 03:09 avm2-libstdc++.l.bc
ちょっと寄り道、します。
avm2-libc.l.bc をディスアセンブルしてみましょう!$ cd AlchemyToolkit/alchemy-darwin-v0.4a/ $ bin/llvm-dis avm2-libc/lib/avm2-libc.l.bcおお。成功したみたいです。 avm2-libc.l.ll というファイルが出来ました。
LLVM というものを全然しらないのですが、そのファイルの中身を見てみると冒頭に ActionScript 3.0 のソースコードみたいなものが見えます。そして、アセンブラのようなものが見えます。
きっと LLVM のアセンブラなのでしょう。; ModuleID = 'avm2-libc/lib/avm2-libc.l.bc' target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-s0:0:64-f80:128:128"target triple = "i686-apple-darwin8" module asm "import flash.utils.*" module asm "import flash.display.*" module asm "import flash.text.*" module asm "import flash.events.*" module asm "import flash.net.*" module asm "import flash.system.*" module asm "" module asm "public var gdomainClass:Class;" module asm "public var gshell:Boolean = false;" module asm "" module asm "public function establishEnv():void" module asm "{" module asm " try" module asm " {" module asm " var ns:Namespace = new Namespace(\22avmplus\22);" module asm " " module asm " gdomainClass = ns::[\22Domain\22];" module asm " gshell = true;" module asm " }" : :初見だと、どこがエントリーポイントかもわかりませんね
寄り道が過ぎたので、インストールに取りかかりましょう。
まず、 AlchemyToolkit/alchemy-darwin-v0.4a に移動します。
$ cd AlchemyToolkit/alchemy-darwin-v0.4a
で、 ./config を実行します。
$ ./config Generating alchemy-setup... Turning execution bit on for Alchemy binaries... Add "source /Users/amachang/AlchemyTest/AlchemyToolkit/alchemy-darwin-v0.4a/alchemy-setup" to your login script. "alc-home" takes you to the Alchemy install folder. "alc-on" puts Alchemy gcc toolchain replacements at the front of your path. "alc-off" restores original path. "alc-util" shows you various Alchemy-related environment vars You need Flash 10 or AIR 1.5 and the Flex 3.2 SDK installed for testing.
alchemy-setup というファイルが作られました。「このファイルを login スクリプトに入れろ」と言われますが、とりあえず使うだけなので手動で実行するだけにします。
$ source alchemy-setup
そうすると、 alc-on alc-off が使えるようになります。これは、実際は環境変数 PATH などを切り替えることをします。
alc-on にした状態で gcc と叩くと achacks/gcc が実行されるといった具合ですね。
というわけで、 alc-on を実行します。
$ alc-on
環境変数を見てみましょう。
$ echo $PATH /Users/amachang/AlchemyTest/AlchemyToolkit/alchemy-darwin-v0.4a/achacks:/Users/amachang/AlchemyTest/AlchemyToolkit/alchemy-darwin-v0.4a/bin:/Users/amachang/AlchemyTest/FlexSDK/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin:/opt/local/bin
先頭に、 achacks ディレクトリが追加されていますね。 alc-off を実行すれば環境変数は元に戻ります。
次は、 sample をコンパイルして実行してみたいですね。
サンプルをコンパイルして実行する
サンプルは、 Alchemy Toolkit の直下の samples ディレクトリにありました。
$ cd AlchemyToolkit/alchemy-darwin-v0.4a/samples/ $ ls -la total 0 drwxr-xr-x 6 amachang staff 204 11 14 03:09 . drwxr-xr-x 9 amachang staff 510 11 18 21:39 .. drwxr-xr-x 2 amachang staff 170 11 14 03:09 AS3API drwxr-xr-x 2 amachang staff 204 11 14 03:09 AS3Lib drwxr-xr-x 2 amachang staff 102 11 14 03:09 HelloFlash drwxr-xr-x 3 amachang staff 204 11 14 03:09 stringecho
HelloFlash
まずは、 HelloFlash を実行してみましょう。
HelloFlash ディレクトリの中には HelloFlash.c というファイルが入っていました。
内容はこんな感じ
#include <stdlib.h> #include <stdio.h> int main(int argc, char* argv[]) { printf("Hello Flash!\n"); }
$ cd HelloFlash $ gcc HelloFlash.c WARNING: While resolving call to function 'main' arguments were dropped! 45416.achacks.swf, 354510 bytes written
なんか、 WARNING が出ましたが成功したみたいです。
そして、 a.exe というファイルが出来ました。
$ ls -la total 704 drwxr-xr-x 2 amachang staff 136 11 18 21:51 . drwxr-xr-x 6 amachang staff 204 11 14 03:09 .. -r--r--r-- 1 amachang staff 104 11 14 03:09 HelloFlash.c -rwxr-xr-x 1 amachang staff 354590 11 18 21:51 a.exe
何も考えずに実行してみましょう。ドキドキ
$ ./a.exe /Users/amachang/AlchemyTest/FlexSDK/bin/adl _sb_45518/app.xml 2> /tmp/adl.trace & echo $! Hello Flash!
おおおお。ちゃんと Hello Flash! と表示されました!
ちょっと寄り道
この a.exe っていうのがどういうファイルなのか気になったので調べてみる$ file a.exe a.exe: a /Users/amachang/AlchemyTest/AlchemyToolkit/alchemy-darwin-v0.4a/bin/swfbridge script text executableswfbridge というコマンドで実行されるスクリプト?開いてみる。
バイナリだった。。$ head -3 a.exe #!/Users/amachang/AlchemyTest/AlchemyToolkit/alchemy-darwin-v0.4a/bin/swfbridge FWS (バイナリ)って感じのファイル。
FWS って swf ファイルのマジックだったっけ?
そんな感じのファイルらしい。 swfbridge ってどんなものなんだろう。 adl を呼び出してるみたいだけど。
app.xml っていうファイルを自動で作って adl を呼び出すツールかな
ちょっと strings してみた$ strings /Users/amachang/AlchemyTest/AlchemyToolkit/alchemy-darwin-v0.4a/bin/swfbridge PATH Couldn't write setup _sb_%d Couldn't create temp dir error: %s pid%d %s/app.xml <application xmlns="http://ns.adobe.com/air/application/1.5"> <id>com.adobe.%s</id> <version>1.0</version> <filename>%s</filename> <name>%s</name> <initialWindow> <x>0</x> <y>0</y> <visible>false</visible> <content>app.swf</content> </initialWindow> </application> %s/app.swf SWF: %s :おおお。 XML はけーん。 a.exe から app.xml と app.swf を作って adl に渡すみたいだなー。
またもや脱線しすぎました。
stringecho
次のサンプルは stringecho です。
$ cd ../stringecho
とりあえず、ディレクトリ構成は以下のようは感じになっています。
$ tree . |-- as3 | `-- EchoTest.as |-- readme.txt `-- stringecho.c 1 directory, 3 files
readme.txt がありますね。でも、読んじゃうともったいないので、何も考えずにコンパイルしてみます。
$ gcc stringecho.c WARNING: While resolving call to function 'main' arguments were dropped! 46476.achacks.swf, 355002 bytes written
なんか、成功しました。では、実行します。ドキドキ
$ ./a.exe
おおおお
AIR さんに怒られました!これはこれで嬉しいですね。
というわけで、 readme.txt を読みます。
なるほど、以下のように swc オプションを付けることで、 swf から使える *.swc ファイルになるようになるようです。
$ gcc stringecho.c -swc -o stringecho.swc WARNING: While resolving call to function 'main' arguments were dropped! 47506.achacks.swf, 362570 bytes written frame rate: 60 frame count: 1 69 : 4 72 : 362500 76 : 33 1 : 0 0 : 0 frame rate: 24 frame count: 1 69 : 4 77 : 506 64 : 31 63 : 16 65 : 4 9 : 3 41 : 26 82 : 471 1 : 0 0 : 0 adding: catalog.xml (deflated 75%) adding: library.swf (deflated 61%)
で、 stringecho.swc ファイルが出来るので、 EchoTest.as のコンパイル時に指定してあげます。
$ mxmlc -library-path+=stringecho.swc --target-player=10.0.0 as3/EchoTest.as
そうすると、 as3/EchoTest.swf が出来ます。
ただ、一見これを実行しても何もおきません。
以下のように、 trace 関数に結果を渡しているので、結果を見るには mm.cfg とかめんどくさい設定をしなければならないからです。
package { import flash.display.Sprite; import cmodule.stringecho.CLibInit; public class EchoTest extends Sprite { public function EchoTest() { var loader:CLibInit = new CLibInit; var lib:Object = loader.init(); trace(lib.echo("foo")); } } }
てっとり早く結果を見たいので、わざと Error を発生させましょう。
package { import flash.display.Sprite; import cmodule.stringecho.CLibInit; public class EchoTest extends Sprite { public function EchoTest() { var loader:CLibInit = new CLibInit; var lib:Object = loader.init(); // 結果をエラーに乗せる throw Error(lib.echo("foo")); } } }
というわけで、これで出来た EchoTest.swf をブラウザで開きます。
おおおお。ちゃんとError に foo という文字列が運ばれてきましたね!
この swf は以下のように swfbridge でも実行出来るようです。
$ ../../bin/swfbridge as3/EchoTest.swf
ちなみに、 C 言語側のコードはこんな感じです。 main で初期化して C 言語の関数を Flash から扱えるようにしているみたいですね。
//Simple String Echo example //mike chambers //mchamber@adobe.com #include <stdlib.h> #include <stdio.h> //Header file for AS3 interop APIs //this is linked in by the compiler (when using flaccon) #include "AS3.h" //Method exposed to ActionScript //Takes a String and echos it static AS3_Val echo(void* self, AS3_Val args) { //initialize string to null char* val = NULL; //parse the arguments. Expect 1. //pass in val to hold the first argument, which //should be a string AS3_ArrayValue( args, "StrType", &val ); //if no argument is specified if(val == NULL) { char* nullString = "null"; //return the string "null" return AS3_String(nullString); } //otherwise, return the string that was passed in return AS3_String(val); } //entry point for code int main() { //define the methods exposed to ActionScript //typed as an ActionScript Function instance AS3_Val echoMethod = AS3_Function( NULL, echo ); // construct an object that holds references to the functions AS3_Val result = AS3_Object( "echo: AS3ValType", echoMethod ); // Release AS3_Release( echoMethod ); // notify that we initialized -- THIS DOES NOT RETURN! AS3_LibInit( result ); // should never get here! return 0; }
自分でも何か作ってみる
最初に作った Src ディレクトリに移動して
$ cd /Users/amachang/AlchemyTest/Src
まずは hello
hello.c を書く
#include "AS3.h" static AS3_Val hello(void* self, AS3_Val args) { return AS3_String("Hello, world."); } int main() { AS3_Val method = AS3_Function( NULL, hello ); AS3_Val object = AS3_Object( "hello: AS3ValType", method ); AS3_Release( method ); AS3_LibInit( object ); return 0; }
Hello.as
package { import flash.display.Sprite; import cmodule.hello.CLibInit; public class Hello extends Sprite { public function Hello() { throw Error(new CLibInit().init().hello()); } } }
で、コンパイル
$ gcc hello.c -swc -o hello.swc && mxmlc -library-path+=hello.swc --target-player=10.0.0 Hello.as WARNING: While resolving call to function 'main' arguments were dropped! 49833.achacks.swf, 358742 bytes written frame rate: 60 frame count: 1 69 : 4 72 : 358677 76 : 28 1 : 0 0 : 0 frame rate: 24 frame count: 1 69 : 4 77 : 506 64 : 31 63 : 16 65 : 4 9 : 3 41 : 26 82 : 471 1 : 0 0 : 0 adding: catalog.xml (deflated 75%) adding: library.swf (deflated 60%) 設定ファイル "/Users/amachang/AlchemyTest/FlexSDK/frameworks/flex-config.xml" をロードしています /Users/amachang/AlchemyTest/Src/Hello.swf (142645 bytes)
クロージャを渡す
AS 側からクロージャを渡して
package { import flash.display.Sprite; import cmodule.hello.CLibInit; public class Hello extends Sprite { public function Hello() { var message:String = "Hello, "; new CLibInit().init().hello(function(message2:String):* { message += message2; }); throw Error(message); } } }
C 言語側で実行
#include <stdio.h> #include <stdlib.h> #include "AS3.h" static AS3_Val hello(void* self, AS3_Val args) { AS3_Val callback; AS3_ArrayValue(args, "AS3ValType", &callback); AS3_Call(callback, AS3_Undefined(), AS3_Array("StrType", " world.")); return 0; } int main() { AS3_Val method = AS3_Function( NULL, hello ); AS3_Val object = AS3_Object( "hello: AS3ValType", method ); AS3_Release( method ); AS3_LibInit( object ); return 0; }
Alchemy の gcc は何をしているか
hoge.c が hoge.swc になる過程を順に見て行きましょう。
1. C/C++ を LLVM 用のバイナリにコンパイルする
$ llvm-gcc -v -emit-llvm -nostdinc -I/Users/amachang/AlchemyTest/AlchemyToolkit/alchemy-darwin-v0.4a/avm2-libc/include -I/usr/local/include --include /Users/amachang/AlchemyTest/AlchemyToolkit/alchemy-darwin-v0.4a/avm2-libc/avm2/AVM2Env.h hoge.c -c -o hoge.o
まずは、このように hoge.c が hoge.o にコンパイルされます。
この hoge.o は LLVM のバイトコードです。
2. avm2-libc.l.bc とリンクする
$ llvm-ld -o=hoge -O5 -internalize-public-api-list=_start,malloc,free,__adddi3,__anddi3,__ashldi3,__ashrdi3,__cmpdi2,__divdi3,__fixdfdi,__fixsfdi,__fixunsdfdi,__fixunssfdi,__floatdidf,__floatdisf,__floatunsdidf,__iordi3,__lshldi3,__lshrdi3,__moddi3,__muldi3,__negdi2,__one_cmpldi2,__qdivrem,__adddi3,__anddi3,__ashldi3,__ashrdi3,__cmpdi2,__divdi3,__qdivrem,__fixdfdi,__fixsfdi,__fixunsdfdi,__fixunssfdi,__floatdidf,__floatdisf,__floatunsdidf,__iordi3,__lshldi3,__lshrdi3,__moddi3,__muldi3,__negdi2,__one_cmpldi2,__subdi3,__ucmpdi2,__udivdi3,__umoddi3,__xordi3,__subdi3,__ucmpdi2,__udivdi3,__umoddi3,__xordi3,__error /Users/amachang/AlchemyTest/AlchemyToolkit/alchemy-darwin-v0.4a/avm2-libc/lib/avm2-libc.l.bc hoge.o
次に、 llvm-ld でリンクされて hoge と hoge.bc が出来ます。( hoge はただ単に hoge.bc を実行する sh ファイル)
この avm2-libc.l.bc C 言語側に libc と同じ API を提供しているようですね。
3. llc というツールで ActionScript のコードを作る
$ llc -march=avm2 -avm2-use-memuser -o=hoge.as -avm2-package-name=cmodule.hoge hoge.bc
このツールで LLVM のコードが、なんと ActionScript のコードになります。
package cmodule.hoge { // Start of file scope inline assembly import flash.utils.* import flash.display.* import flash.text.* import flash.events.* import flash.net.* import flash.system.* public var gdomainClass:Class; public var gshell:Boolean = false; public function establishEnv():void { try { var ns:Namespace = new Namespace("avmplus"); gdomainClass = ns::["Domain"]; gshell = true; } catch(e:*) {} if(!gdomainClass) { var ns:Namespace = new Namespace("flash.system"); gdomainClass = ns::["ApplicationDomain"]; } } establishEnv(); public var glogLvl:int = Alchemy::LogLevel; public function log(lvl:int, msg:String):void { if(lvl < glogLvl) : : (27436行)
4. asc.jar で、 as をコンパイルする
$ java -Xms16M -Xmx1024M -jar /Users/amachang/AlchemyTest/AlchemyToolkit/alchemy-darwin-v0.4a/bin/asc.jar -AS3 -strict -import /Users/amachang/AlchemyTest/AlchemyToolkit/alchemy-darwin-v0.4a/flashlibs/global.abc -import /Users/amachang/AlchemyTest/AlchemyToolkit/alchemy-darwin-v0.4a/flashlibs/playerglobal.abc -d -config Alchemy::Shell=false -config Alchemy::NoShell=true -config Alchemy::LogLevel=0 -config Alchemy::Vector=true -config Alchemy::NoVector=false -config Alchemy::SetjmpAbuse=false -swf cmodule.hoge.ConSprite,800,600,60 hoge.as
5. hoge.swf から ActionScript のバイトコードを取り出す
$ GetABC2.pl hoge hoge.swf.abc
6. もっかい swf を作る
$ PutABC2.pl /Users/amachang/AlchemyTest/AlchemyToolkit/alchemy-darwin-v0.4a/achacks/swctmpl.swf temp.swf hoge.swf.abc cmodule/hello/CLibInit $ V10SWF.pl temp.swf library.swf
7. catalog.xml を付けて zip で固めて swc にする
ちなみに、 catalog.xml はこんなの
<?xml version="1.0" encoding ="utf-8"?> <swc xmlns="http://www.adobe.com/flash/swccatalog/9"> <versions> <swc version="1.0" /> <flex version="2.0" build="143452" /> </versions> <features> <feature-script-deps /> <feature-files /> </features> <libraries> <library path="library.swf"> <script name="cmodule/hello/CLibInit" mod="1177909560000" > <def id="cmodule.hello:CLibInit" /> <dep id="Date" type="e" /> <dep id="Date" type="s" /> <dep id="flash.utils:Dictionary" type="e" /> <dep id="flash.utils:Dictionary" type="s" /> : (略) : <dep id="AS3" type="n" /> <dep id="Object" type="i" /> </script> </library> </libraries> <files> </files> </swc>
$ zip hoge.swc catalog.xml library.swf
おおおおおおおお。と言う訳で hoge.swc が出来ました。
まとめ
- Alchemy を使うと結構簡単に C 言語のコードを swc にして使うことができる
- ただ、僕の環境では C++ をコンパイルしようと思っても Alchemy の include ファイル内でエラーが出来なかった。
- C 言語はいったん LLVM のバイトコードになり、最適化されたあと、 ActionScript のコードに変換され、それがコンパイルされる。
- C 言語 (hoge.c)
- LLVM バイトコード (hoge.bc)
- ActionScript のコード (hoge.as)
- ActionScript Byte Code (hoge.abc)
- Swc (hoge.swc)
最後に
Alchemy 楽しいです!
みなさんも遊んでみてはいかがでしょうか。
Firefox メモ
alert の位置を知る
- dom/src/base/nsGlobalWindow.cpp 4035 行目
- nsGlobalWindow::Alert
JavaScript を書く
alert(0); var img = document.createElement('img'); img.onload = function() { alert(1) }; alert(2); img.src = 'http://www.hatena.ne.jp/images/top/h1.gif'; alert(3); document.body.appendChild(img); alert(4);
Firefox で開く
0 -> 2 -> 3 -> 1 -> 4
gdb 起動(Firefox のビルドが必要)
$ gdb MinefieldDebug.app/Contents/MacOS/firefox-bin (gdb) b nsGlobalWindow::Alert (gdb) r
alert(1) のところだけ見る
var img = document.createElement('img'); img.onload = function() { alert(1) }; img.src = 'http://www.hatena.ne.jp/images/top/h1.gif'; document.body.appendChild(img);
バックトレース
#0 nsGlobalWindow::Alert (this=0x1c97f420, aString=@0x18cc38d0) at /Users/amachang/mozilla/dom/src/base/nsGlobalWindow.cpp:4035 : : #16 0x1314fcf1 in nsImageLoadingContent::Event::Run (this=0x17f93040) at /Users/amachang/mozilla/content/base/src/nsImageLoadingContent.cpp:833 : :
nsImageLoadingContent::Event::Run のコードを見てみる
(gdb) b nsImageLoadingContent::Event::Run (gdb) c Breakpoint 2, nsImageLoadingContent::Event::Run (this=0x17e55150) at /Users/amachang/mozilla/content/base/src/nsImageLoadingContent.cpp:823 823 if (mMessage.EqualsLiteral("load")) { (gdb) l 818 NS_IMETHODIMP 819 nsImageLoadingContent::Event::Run() 820 { 821 PRUint32 eventMsg; 822 823 if (mMessage.EqualsLiteral("load")) { 824 eventMsg = NS_LOAD; 825 } else { 826 eventMsg = NS_LOAD_ERROR; 827 } (gdb) l 828 829 nsCOMPtr<nsIContent> ourContent = do_QueryInterface(mContent); 830 831 nsEvent event(PR_TRUE, eventMsg); 832 event.flags |= NS_EVENT_FLAG_CANT_BUBBLE; 833 nsEventDispatcher::Dispatch(ourContent, mPresContext, &event); 834 835 return NS_OK; 836 } 837 (gdb)
うーん。送信してるところが見たい
(gdb) reverse-search "load" 823 if (mMessage.EqualsLiteral("load")) { (gdb) reverse-search "load" 249 FireEvent(NS_LITERAL_STRING("load"));
あった!
249 行目で止めてみる
(gdb) b nsImageLoadingContent.cpp:249 (gdb) c Breakpoint 4, nsImageLoadingContent::OnStopDecode (this=0x1c6dd39c, aRequest=0x1c6dbf80, aStatus=5505024, aStatusArg=0x0) at /Users/amachang/mozilla/content/base/src/nsImageLoadingContent.cpp:249 249 FireEvent(NS_LITERAL_STRING("load")); (gdb)
バックトレース
Breakpoint 4, nsImageLoadingContent::OnStopDecode (this=0x1c6dd39c, aRequest=0x1c6dbf80, aStatus=5505024, aStatusArg=0x0) at /Users/amachang/mozilla/content/base/src/nsImageLoadingContent.cpp:249 249 FireEvent(NS_LITERAL_STRING("load")); (gdb) bt #0 nsImageLoadingContent::OnStopDecode (this=0x1c6dd39c, aRequest=0x1c6dbf80, aStatus=5505024, aStatusArg=0x0) at /Users/amachang/mozilla/content/base/src/nsImageLoadingContent.cpp:249 : : #10 0x13227d41 in nsHTMLImageElement::SetSrc (this=0x1c6dd380, aValue=@0xbfff99a4) at /Users/amachang/mozilla/content/html/content/src/nsHTMLImageElement.cpp:219 #11 0x11223f98 in nsIDOMHTMLImageElement_SetSrc (cx=0xb67000, obj=0x1cc65340, id=441507292, vp=0xbfffa5f8) at dom_quickstubs.cpp:6846 : : #64 0x000026e3 in main (argc=1, argv=0xbffff8e8) at /Users/amachang/mozilla/browser/app/nsBrowserApp.cpp:156
ながいなー
よーく見ると
nsHTMLImageElement::SetSrc から load イベントが発火されていることが分かる
でも
alert(0); var img = document.createElement('img'); img.onload = function() { alert(1) }; alert(2); img.src = 'http://www.hatena.ne.jp/images/top/h1.gif'; // ここで発火されるのに alert(3); // ここが alert(1) より先に起こるのは何故? document.body.appendChild(img); alert(4);
alert(3) で止めてバックトレースを見る
Breakpoint 1, nsGlobalWindow::Alert (this=0x1c97f420, aString=@0x1e75d860) at /Users/amachang/mozilla/dom/src/base/nsGlobalWindow.cpp:4035 4035 FORWARD_TO_OUTER(Alert, (aString), NS_ERROR_NOT_INITIALIZED); (gdb) p aString $7 = (const nsAString_internal &) @0x1e75d860: { mData = 0x1e66a898, mLength = 1, mFlags = 5 } (gdb) p aString->mData $8 = (PRUnichar *) 0x1e66a898 (gdb) p (char*)aString->mData $9 = 0x1e66a898 "3" (gdb) bt #0 nsGlobalWindow::Alert (this=0x1c97f420, aString=@0x1e75d860) at /Users/amachang/mozilla/dom/src/base/nsGlobalWindow.cpp:4035 #1 0x0049e1c1 in NS_InvokeByIndex_P (that=0x1c97f420, methodIndex=65, paramCount=1, params=0xbfff84e4) at /Users/amachang/mozilla/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_unixish_x86.cpp:179 : :
うーん。
別スレッドなのかな?
全スレッドバックトレース
(gdb) thread apply all bt
でも、 alert(1) も alert(2) も main から繋がってたから。たぶん同じスレッドだと思うんだけどなー
あそっか
イベントは発火されてもすぐには実行されなくて、しかるべきタイミングでイベントのキューを見に行くのか。
そのタイミングってどこよ
もっかい nsImageLoadingContent::Event::Run で止めてみる
Event::Run のバックとレースを見てみると alert(3) の nsGlobalWindow::Alert から呼ばれていることが分かった
#0 nsImageLoadingContent::Event::Run (this=0x1d67fe50) at /Users/amachang/mozilla/content/base/src/nsImageLoadingContent.cpp:823 #1 0x004833c0 in nsThread::ProcessNextEvent (this=0x719030, mayWait=1, result=0xbfffadfc) at /Users/amachang/mozilla/xpcom/threads/nsThread.cpp:510 #2 0x0040d198 in NS_ProcessNextEvent_P (thread=0x719030, mayWait=1) at nsThreadUtils.cpp:227 #3 0x1298c2da in nsXULWindow::ShowModal (this=0x16991bc0) at /Users/amachang/mozilla/xpfe/appshell/src/nsXULWindow.cpp:396 : : #10 0x133b6904 in nsGlobalWindow::Alert (this=0x1c53aa10, aString=@0x1d69f080) at /Users/amachang/mozilla/dom/src/base/nsGlobalWindow.cpp:4064
でも、なんで XULWindow::showModal が NS_ProcessNextEvent_P を呼び出すかが分からない
NS_ProcessNextEvent_P (マクロ名 NS_ProcessNextEvent)を呼び出してるところを grep してみるとたくさんあることに気がつく。
これらのタイミングでイベントは処理されるのか。
- alert されたとき
- window.open したとき
- HTML のパースが終わったとき
などなど
つまり、
var img = document.createElement('img'); img.onload = function() { alert(1) }; img.src = 'http://www.hatena.ne.jp/images/top/h1.gif'; // この時点で画像のロードが終わっていて alert(3); // この alert に誘発されて onload イベントハンドラが起動した document.body.appendChild(img);
ってことか
じゃあ、 Firefox の画像って非同期にロードされてる訳じゃないのかな?
試しに、以下のような CGI を作ってみる
#! /usr/bin/perl print "Content-Type: image/gif\n\n"; # 10 秒停止 sleep(10); open IMG, "hogehogehogehoge.gif"; binmode IMG; binmode STDIO; while (my $bin = <IMG>) { print STDOUT $bin; } close IMG;
で、以下のような JavaScript からこの image.cgi を起動
var start = new Date; var img = document.createElement('img'); img.onload = function() { alert(1) }; img.src = 'image.cgi'; document.body.appendChild(img); alert(4);
お。ちゃんと非同期になってるなー
とりあえず
今日はこの辺で、結局なんで Firefox の img.src の設定が遅いかは分からなかった
1000 万行のソースコード
Linuxカーネルのコード行数、1000万行を上回る | スラド
1 日 1000 行読んだとしても 30 年かかるのかあ。
桁が違いすぎる。
その間もソースコードは増え続ける。
やがて、誰も保守することができなくなったソースコードの山。
人類はソースコードを解析するという目的で、ソースコードを理解するプログラムを作った。
そして、コンピュータは自立進化を始める。
これがコンピュータ対人類の戦争の始まりだったのだ
なーんて