Top Gearについて

この記事はカレーのち ぴょこりんクラスタ Advent Calendar 2018のために書いたものです。 ちなみにこのAdvent Calendarが何なのかについては、主催者による紹介記事を見てください。

サマリ

自分の大好きなTop Gearについて書く。

Top Gearについて

Top Gearとは、イギリスBBC作の自動車好きのためのTV番組である。

Jeremy Clarkson、Richard Hammond、James Mayの3人*1が、 自分の趣味を全面に押し出して、自動車を紹介する。 紹介すると言っても、そこらのグルメ番組とは異なり、 単においしさのリアクション芸を競うのではなく、何がよくて何がよくないかを 試乗した上できちんと両方伝える。もちろん、ここの判断基準にも趣味が反映されるので 大衆の自動車選択には全く役にたたないが、見ていて気分がよい。 自動車の紹介だけでなく、自動車を使ったユニークな*2企画が 最高におもしろい番組である。

今回はTop Gearのうち、特に印象に残っているエピソードを、出演者の名言とともに紹介する。

Season 21 Episode 1

概要

1980~1990年代の古き良きホットハッチ*3を紹介する回。 各自が限られた予算の中でお気に入りのホットハッチを購入し、課題にチャレンジする。

ちなみに、それぞれ以下の車を選んでいる。

  • Jeremy
  • Hammond*5
    • Vauxhall Nova SRi*6
    • 700ポンドで購入
  • James
    • Ford Fiesta XR2i 16V*7
    • 750ポンドで購入

名言

  • タイヤよりもルーフで走る時間が長い車だ
    • Jeremyの、HammondのNovaに対する発言。このNovaは本当にひどくて、運転席側ドアと助手席側ドアが全く違うものになっており、 前の持ち主がどのように扱っていたかがわかるような状態になっていた。
  • そもそもNovaは買う必要はなく、盗めばよい車です
    • Jeremyの、HammondのNovaに対する発言。キーなしでも簡単にエンジンがかかってしまうような車であり、 気持ちはわからんでもない。
  • スーパーマーケットを襲う際に、Golfの盗難防止アラームが突然鳴り出す
    • "80年代は車泥棒が多かったので、各自が買った車でスーパーマーケットを襲う"というコンセプトのもと、 スーパーマーケット然としたセットの中を走り抜けるタイムを競う企画での出来事。 走り出す前にタイミングよく"ピューイwwwwピューイwwww"みたいな感じで突然鳴り出したのが笑えるので、 名言ではないけどこの枠に入れた。
  • ファミリーといえば、元気そのものの姉を埋葬しかけたそうだね?
    • Jeremyの、ゲストのヒュー・ボネヴィルに対する発言。ヒュー・ボネヴィルが小さいころの微笑ましい出来事の話。

Season 21 Episode 3

概要

一般人が買える車をレビューしろという苦情が入ったためか、コンパクトカー特集。 コンパクトカー(1リッター3気筒)に乗ってクリミア半島に向かう途中で様々な課題にチャレンジする。

各自が選んだ車は以下。Volkswagen Up!は日本でもたまに走っているのを見かけるが、 あれを見るたびにカールおじさんを思い出すんだよなあ。

  • Jeremy: Volkswagen Up!
  • Hammond: Ford Fiesta
  • James: Dacia Sandero

名言

  • 道路沿いの"カフェ"へ
    • 全くカフェではない。道の駅にある農産物を直販しているような感じの場所。
  • ドバシ ノギ
    • 伝えたかったこと: 魚以外の食べ物をください
    • 実際の意味: Where is your legs?
    • Jeremyの、"カフェ"のお姉さんに対する発言。運転中の暇を持て余したJeremyが、ウクライナ語の 語学教材で練習した成果を見せようとした場面。笑ってはいけないシリーズにおける ジミー大西を彷彿とさせる意味のわからなさが笑える。お姉さんも苦笑い。
  • ヴァシ スーベニーァビ
    • 伝えたかったこと: りんごをください
    • 実際の意味: I will eat your sourvenirs
    • これもドバシ ノギと同じ文脈。
  • Jeremyはりんごを1個注文
    • 出てきたものは生キャベツ1玉。その後キャベツをかじるJeremyに、この番組の妥協のなさが感じられて本当によい。

まとめ

これらは全部Amazon Prime Videoで見られますので、直接名言を感じてください。

*1:何回かメンバーが変わっているが、自分が見たことがあるのはこの3人のとき

*2:やることはもちろん会話さえもめちゃくちゃな

*3:https://ja.wikipedia.org/wiki/%E3%83%9B%E3%83%83%E3%83%88%E3%83%8F%E3%83%83%E3%83%81

*4:https://en.wikipedia.org/wiki/Volkswagen_Golf_Mk2

*5:何故かHammondだけHammondと呼ばれている

*6:https://en.wikipedia.org/wiki/Opel_Corsa

*7:http://www.guide-autosport.com/guide/ford-fiesta-xr2i-16v.php

ある合宿参加者の記録

この記事はカレーのち ぴょこりんクラスタ Advent Calendar 2018のために書いたものです。 ちなみにこのAdvent Calendarが何なのかについては、主催者による紹介記事を見てください。

サマリ

  • ぴょこりんクラスタ 夏の大ハッカソン(仮)*1が10月開催
  • そのときの作業ログをもとに合宿を振り返りと、今だから言えるコメントを追記する

合宿における自分の役割

  • 会議室予約、ホテル予約、会計
  • 参加者

参加者(全部はてなidで表記、順不同)

企画者によるメモ

合宿で何をやったか

これの前身を作っていた。

合宿前日以前

  • 09/13

    • 宿泊地が決まり、予約を取る
  • 09/22

    • darumapからしおりが届く
    • テルチェックイン前の作業スペースとして、貸会議室を予約する
  • 10/08

    • LT用の原稿を作成する
    • markdownをpandocでスライドにしようとして、フォント設定でちょっとはまる
    • 最終的にこれをヘッダとして差し込んで解決した。WSLでやってたのでYu Gothic使いたかったけど、 Source Han Serifはきれいなフォントだったのであきらめることとした。
    • Windows 10 Homeだと、野良exeファイルを拡張子に紐付けられないことに気づかず*2、1時間くらい消費した。
---
title: Pyokkorin hackathon camp first lightning talk
author: nbisco
date: 2018/10/27
header-includes:
    - \usepackage{luatexja-otf}
    - \usepackage{luatexja-fontspec}
    - \setmainjfont{Source Han Serif}
    - \setsansjfont{Source Han Sans JP}
    - \hypersetup{unicode=true}
    - \renewcommand{\kanjifamilydefault}{\gtdefault}
---

合宿日当日

  • 1日目 10:00 合宿開始
    • みんな時間どおり集まり、ライトニングトークを始める
    • 自分のPCはUSB Type-CもしくはType-Aしかなくて、プロジェクタにつなぐのに手間取った。 ドライバ落としてきたらなんとかなった。
    • 各自担当分野が全く違うので、話を聞いてて楽しい。ただし、助け合いは無理そう。
  • 1日目 11:15 ライトニングトーク終了、作業開始
  • 1日目 12:08 手作業による/proc/{pid}/memの読み書きを確認
    • readelfで読み取った値を手計算してアドレス算出
    • 環境は以下
  • 1日目 12:13 DWARFについての勉強着手

    • 今だから言える:ここですでに方向性を間違えている。自分が必要だったのはシンボルテーブルの内容であって、デバッグ情報じゃなかった。 以降、ゴールに関係ない迷走で2時間消滅する。
    • 参考資料1: https://qiita.com/mhiramat/items/8df17f5113434e93ff0c
    • 参考資料2: http://www.dwarfstd.org/Home.php
    • DWARF表現: DWARF内でlocationを表現するために用いるスタックマシンに対する演算命令(せっかくmarkdownで表形式にしたので載せておく)
      • reg0、reg1、...、reg31: 汎用レジスタ。個々のアーキと対応付けられる。
      • regx: 汎用レジスタを示すオペコード。引数がレジスタ番号になる(例: regx 0はreg0)
      • fbreg: フレームレジスタの値からの相対オフセットでメモリ上に保存されている場所を示すためのオペコード。 オフセットを表す引数を1つ取る。
      • breg0、breg1、...、breg31: 汎用レジスタからの相対オフセットでメモリ上に保存されている場所を示す。 オフセットを表す引数を1つ取る。
      • bregx: 汎用レジスタからの値からの相対オフセットでメモリ上に保存されている場所を示す。 このオペコードはレジスタ番号とオフセットを表す引数を2つ取る。
      • addr, const*: アドレス値や定数値を示すオペコード。仮引数が定数の場合などに使われる。
      • x86のアドレスとの対応(http://www.ucw.cz/~hubicka/papers/abi/node14.html)
      dwarf x86_64 memo
      r0 rax general purpose
      r1 rdx general purpose
      r2 rcx general purpose
      r3 rbx general purpose
      r4 rsi general purpose
      r5 rdi general purpose
      r6 rbp Frame Pointer
      r7 rsp Stack Pointer
      r8-15 r8-r15 extended integer register
      r16 return address
      r17-24 xmm0-7 SSE registers
      r25-32 xmm8-15 Extend SSE
      r33-40 st0-7 floating point register
      r41-48 mm0-7 MMX register
  • 1日目 14:00 会議室から出て移動

  • 1日目 16:22 宿チェックイン
    • 部屋は広いし、無線LANはそこそこ速いしでよかった
    • 眺めもよい
  • 1日目 17:00 libdwの使い方がさっぱりわからないので、src/addr2line.cを覗いてみるも、やっぱりよくわからない
    • 今だから言える:この時点でも迷走は続いている。libdwなんて見る必要なかった。
  • 1日目 18:00くらい libdwを使うのは一旦諦め、小さいサンプルが手に入ったlibbfdを使うことにする
  • 1日目 18:24 libbfdのサンプルをビルドして期待通り動くことを確認した
    • ツールはこんな感じにする
      • input: グローバル変数名と設定する値
      • output: 以前の値と更新後の値
      • 処理フロー
        1. 対象バイナリのグローバル変数のシンボルとアドレスの一覧を作成する
        2. シンボルと入力されたグローバル変数名のマッチング
        3. マッチングがとれればアドレスを計算して、メモリにI/O
  • 1日目 18:50 darumapを迎えにホテルロビーへ。ついでに晩ごはん。
  • 1日目 20:30 晩飯終了。やはりというかやむなしというか、酒を各自購入して部屋に戻る。
    • Before: これは真面目な会なので酒はNG
    • After: やっぱりなんかアルコールほしい気がするんだよな~~~~~
    • 今だから言える:やはりお祭りとしての側面もあるので仕方ないよね。
  • 1日目 20:35 作業再開
  • 1日目 22:00 darumap、突如キングギドラ公開処刑を大音量で流し始める。僕はリアルなので手を叩いた。
  • 1日目 22:30 温泉タイム。露天風呂に入れないのが残念。
  • 1日目 23:20 再開
  • 1日目 24:05 いろいろあきらめた手法で4Byteの書き換えに成功。動くのが正義だ。
  • 1日目 24:15 区切りがついてしまったので、あきらめて酒を飲み始めた
  • 1日目 24:45 全員に悪い空気が伝染してしまい、mow*3開始。 頑なに酒を拒否していたdarumap、「酒を入れざるを得ない」といいおもむろに飲み始める。
  • 1日目 25:15 2ラウンドして終了
  • 1日目 25:30 就寝
  • 2日目 5:00 起床。だがすぐに2度寝。
  • 2日目 6:30 再度起床。
  • 2日目 6:45 朝食
  • 2日目 7:30 露天風呂
  • 2日目 8:00 作業再開
    • ここでは主にスライドづくり。内容を詳しく知らない人に伝える文章というのは難しいので、訓練が必要だと切実に思った。
    • Google Slidesが書きやすくていい。LibreOfficeなんかよりもずっと軽快に動く。
  • 2日目 9:30 成果発表
    • みんなの力作が見られて本当に楽しかった。自由に質問が出るし、誰も怒る人はいない。
  • 2日目 10:45 チェックアウト
  • 2日目 12:00 昼飯 & 解散
  • 2日目 16:00 帰宅・休憩して作業再開
  • 2日目 17:00 binutilsのソースを覗いたら、libbfdを使わずに実装しました and libbfdでは取れない情報もある みたいなことが書いてあったので諦めてelfutilsを使うか、直接自分でバイナリ読むソース書いたほうがいいんじゃないかという気がする。 しかし今日のところはここまで。疲れた。
  • N日後 これを作ってアドベントカレンダーの記事を作成、今に至る。

反省

  • よかった
    • 知らない分野に触れられた
    • 発表までしたことで理解が深まった
    • 終始集中して作業できた
  • いまいち
    • 調査不足でだいぶ迷走した
  • どちらともとれる
    • 1泊は短いような、ちょうどよいような。
    • みんなで助け合いみたいなこともやってみたかった。助け合いがなかったので集中できたという説はある。

おわりに

学びも成果も大きいのでまたやりたい。

*1:もくもく会だったりいろいろ呼び方があるので仮扱い

*2:あくまで挙動からの推測

*3:みんな知能を失う楽しいゲームです

我が家の家計簿システムについて

この記事はカレーのち ぴょこりんクラスタ Advent Calendar 2018のために書いたものです。 ちなみにこのAdvent Calendarが何なのかについては、主催者による紹介記事をみてください。

サマリ

  • 我が家の家計簿(支出しか記録してないけど)システムについて紹介する
  • 家計簿システムの機能
    • 月ごとの累計支出額計算
    • 月ごとの各自の負担分の計算
    • 支出状況の可視化

はじめに

お金を貯めたい、でも貯まってる感がない。ボーナスでなんとなく貯まることもあれば、 財形で別口座に逃がしているという謎の安心を感じることもある。 そう、俺たちは雰囲気で家計をやりくりしている。

例えば、こんなことはないだろうか。

  • 今月は飲み会多かったけど飲み会だし大したことない
  • 今月は自分へのご褒美だから出費は多少増えているけど費用対効果は高いのでOK
  • 今月はボーナス出たからだいたいプラマイゼロかプラスになっているはず
  • 毎日コーヒーを買うくらいの自由はあるし、まあそんなに高くないから大丈夫でしょ

こんな感じの理屈で自分を納得させたことは数え切れないほどあるはずだ。 これらの共通点は定量性のなさだ。定量的に把握できないから、雰囲気でやりくりすることになってしまう。

お金を使うことは問題ではない。いくら使っているかわからないのが問題である。 いくら使っているのかわからないと、毎月赤字なのか黒字なのかわからない。だから、お金が貯まらない。 そんな状況を脱出するためには、まず自分が何に対して使っているのか知らなければならない。

収支を知るための道具の1つに、家計簿がある。 今回は、家計簿への支出の記録・集計・可視化のために何をやっているかをまとめる。

家計簿システムの要件と実現方式

以下の表にまとめる。

# 分類 要件 実現方式 理由など
1 記録 家族が各自支出を入力できる Zaimの共通アカウントを用意して、それぞれのスマホからZaimアプリで入力する 使ったことがあったから
2 記録 過去データの取得 自作の取得ツール ZaimにはRead onlyだけどAPIがあるから。手動は絶対に続かない。
3 記録 過去データの保存 SQLiteの利用 慣れてるPythonから特別なライブラリ不要で使えて、あとで加工も簡単にできそうだから。何より新しくデータベースをインストールして依存関係を増やすのが嫌だから。
4 集計 各自の支出分と負担分を算出する 自作の計算ツール Zaimの共通アカウントにはそういう機能がないため
5 可視化 集計結果の表化 Google Spreadsheet + 自作のアップロードツール どこからでもブラウザがあれば見られるから。あと、最悪Zaim乗り換えても、Google Spreadsheetは乗り換えないから。
6 可視化 集計結果のグラフ化 Redash SQLiteをデータソースとして選択できて、それなりに開発がされてそうだったから。あと、ブラウザから結果を見るのはもちろん、SQLクエリも書けるから。

我が家の家計簿システムの概要

我が家の家計簿システムの概要は以下の図のとおり。

f:id:nbisco:20181208224442p:plain
家計簿システムの概要

使い方は以下のとおり。

  • 記録・集計
    1. 各自がZaimのアプリから逐次支出を記録
    2. 毎日1回、Zaimのサーバからデータを取得して、SQLite3に反映
    3. 毎月最終日には、その月分の支出をまとめてGoogle Spreadsheetにアップロード
  • 可視化された結果の確認
    1. 月の初日に、各自の支出額、負担額を確認
    2. 週1回くらい*1に今月の支出状況の確認

家計簿システムのソースコード

github.com

Google Sheets APIのサンプルを流用して作ってるので、Apache License。 個人情報っぽいところは全部落とした・・・はず・・・。 正直申し訳ないのは、githubに上げたにも関わらず他人が使えるような状態じゃないところで、 ある意味技術ポエムと言うべきものになっている*2

道具の紹介

ここでは使ってるもの、作ったものについて紹介する。

Zaim

Zaimとは、有名な家計簿アプリの1つ。 我が家では、家族用アカウントを作って、それぞれのスマホから入力するようにしている。 見た目はわかりやすいし、入力にも困らないので、他*3を試すことなく使い続けている。

だいたいにおいて困らないZaimだけど、いくつかいまいちな点がある。例えば以下。

  • APIがRead Onlyなところ。定期的な固定出費(例えば家賃とか)を自動入力したい。 1/19に再確認したら普通にWriteできるAPIがあった。何かと誤解していたのかな?
  • 誰が入力したかわからないところ。そのせいでカテゴリをかなり細かく分けざるを得なかった。
  • カテゴリの追加が面倒。

Google Spreadsheet

もはや説明はいらない。本当に便利でありがたいですね。 家計簿以外に何かを集計したり、作業リスト代わりにしたりと、我が家では大活躍。

SQLite

SQLデータベースエンジンの実装の1つ。データベースエンジンと言っても、単にライブラリPythonさえあれば使えるので、今回のような小規模利用で依存関係増やしたくない場合にちょうどいいと思う。

Redash

データ可視化ツールの1つ。きれいなグラフを作ってくれるし、Dockerイメージも配布されているので導入も簡単。 案外、SQLiteがデータソースに選べる可視化ツールはあまりなくて、選択肢は実質これくらいだったような気がする。

自作ツール

ファイルとやってくれること

以下のとおり。

  • get_zaim_auth_key.py: ユーザが直接実行する
    • ZaimのAPIキーを取得してくれるが、完全自動じゃない(いまいち)
  • db_build.sh: ユーザが直接実行する
    • 現在あるデータベース(固定でzaim.db)を消す
    • SQLiteファイルを作成する(実際に処理をするのはdbgen.py)
    • Zaimから過去データを吸い上げてSQLiteファイルに格納する(実際に処理するのはzaimapi.py)
  • dbgen.py: ユーザが直接実行しない
    • SQLiteファイルを作成する
  • gspread.py: ユーザが直接実行しない
    • Google Sheets APIを叩いて、Zaimから取ってきたデータをアップロードする
  • zaimapi.py: ユーザが直接実行しない
    • Zaim APIを使ってデータを取得する
    • 取得したデータをSQLiteに格納する
  • zaim.py: ユーザが直接実行することもある
    • Zaim APIを使ってデータを取得し、SQLiteを更新する(実際に処理するのはzaimapi.py)
    • 各自の負担分を計算する
    • (指示されたときだけ)Google Sheets APIを使って、データをアップロードする(実際に処理するのはgspread.py)
  • kakeibo.sh: ユーザが直接実行する
    • zaim.pyをキックして出力をログファイルに残す(ずっと残り続けるのでいまいち)
    • 最終日かどうかを判定して、Google Sheets APIを叩くかどうか決める
  • packages.txt
    • pipで使っているパッケージ群

使い方の流れ

以下、全てcloneしたディレクトリ直下にいることを前提とする。 カテゴリ作成と認証が面倒だけど、最初1回だけなので耐える。

Zaimでのカテゴリ作成

負担分を明確にするには、誰が何のために払ったかを明確にする必要がある。 Zaimのいまいちなところは誰が払ったかわからないところなので、そのための情報を カテゴリにいれる。つまり、入力者分だけカテゴリを作らなきゃいけないので重労働が発生する。 さらに、個人出費も記録できるようにするため、さらにその倍カテゴリを作らなければならない。これは苦役である。

githubソースコード上は、alphaさんとbetaさんの2人で記録するという体にしてある。

ZaimのOAuth認証~DB作成まで

一部蛮族的操作が必要なのはすまない気持ちでいっぱいだが、初回1回だけなので耐える。

  1. Zaimの開発者アカウントを作成する(単にログインすればよい)
  2. ここを参考にZaimアプリケーションを登録し、Consumer IDとConsumer Secretを入手する
  3. 入手したConsumer KeyとConsumer Secretを、.credentials/zaim_secret.jsonに以下のように記入する gist.github.com
  4. get_zaim_auth_key.pyを実行すると、認証用URLが表示される。ブラウザでアクセスする。
  5. ログイン後、get_zaim_auth_key.py内のcallback_urlで設定したURLへリダイレクトされるが、リダイレクト先をちゃんと作ってないとアクセスに失敗する。 失敗しても慌てず、ブラウザのURLを見て、oauth_tokenとoauth_verifierをメモする。
  6. .credentials/zaim_secret.jsonを更新する gist.github.com
  7. db_build.shを実行し、SQLiteファイル(zaim.dbという名前で作成する)を作成する。このスクリプトは指定日(YYYY-MM-DDで指定)から今日までの全部のデータを取得するので、 自分が取得したい日を引数で指定する。

Google Spreadsheetの認証~Zaimからデータ取得~Google Spreadsheetへのアップロードまで

  1. これとかこのあたりを読んで、 sheet IDとか、client_secret.jsonを入手しておく。入手したら、gspread.pyのSPREADSHEET_IDとCLIENT_SECRET_FILEを変更する。
  2. ./zaim.py --spreadsheet --noauth_local_webserverを実行すると、Zaimからデータ入手したあと、SQLiteを更新する。
  3. --spreadsheetオプションを付けているので、SQLite更新後、Google Spreadsheetにアップロードしようとするが、初回はOAuthのトークンがないので、 トークン取得処理が走る。ここのサンプルコード実行時みたいに、認証URLが出るので、指示に従う。
  4. 認証がうまくいくと、"YYYY-MM"という名前のシートができていて、以下が入力されているはず。
    • それぞれが支払った金額
    • それぞれが分担する金額
    • 清算金額
    • Zaimで入力したデータ

Redashを用いた可視化

こんな感じのものが日々更新されていく。以下の画像は雰囲気だけの例。

f:id:nbisco:20181209091906p:plain
redashでの表示例

  1. Redashコンテナを起動する。自力で入れてもよいが、依存関係解決がけっこう大変だと思うので、コンテナがおすすめ。
  2. データソースにzaim.dbを指定する。
  3. 自分の好きなようにSQLで集計する。

日々の動作

kakeibo.shを実行する。cronとかに登録しておくと楽でよい。 入力漏れとかでシートを作り直すときは、ブラウザでシートを消してから実行する必要がある(これもいまいちだけどたまになので残っている)。

これを作って変わったこと

  • プラス
    • 自分たちのやりくりの実力がわかるようになった
    • いくら使うかだいたいわかるので、先々の計画が立てやすくなった
    • 何を減らすべきかを雰囲気でなく数字で考えられるようになった
  • マイナス
    • "あっ、こんなになんで使っちゃったんだろう(悲しみ)"みたいな気持ちになってしまうことがある。強い心が必要。

おわりに

支出が追いかけられるようになり、自分たちが雰囲気に流されることは減ったが、 それでも使うときは使っちゃうし、記録できてもダメなときはダメという感じである。 ただ、先々の計画が立てやすくなったのは本当によかった。

*1:最近は気が向いたときになってしまっている・・・

*2:自分の書いた記事に技術ポエムじゃないものが果たしてあっただろうか

*3:例えばマネーフォワードとか

ハリーポッターの感想の変遷

この記事はカレーのち ぴょこりんクラスタ Advent Calendar 2018のために書いたものです。

まとめ

年齢とともに感想は変わるし、名作は何度読んでもおもしろい。

中学生

大学生

  • 爆発したりしない分刺激が足りない
  • 実用的な魔法がそろってて文化の違いを感じる
  • ハリーポッターが生意気だしきちんと教育すべき
  • どうしてスネイプ先生主人公じゃないの???

社会人

  • よく考えてみればポッターはまだ10歳、年齢の割にしっかりしてるし、多少生意気だったり調子に乗るのは仕方ない
  • ダーズリー家に入れた判断は正しかった、こんな調子でちやほやされるとろくなことがなさそう
  • マルフォイ見てると、バック・トゥ・ザ・フューチャーのBiff一味を思い出す。成敗されないところが違う点だな。

おわりに

好きなシーンは何回読んでも変わらなくて、スネイプ先生がダンブルドアの前でエクスペクトパトローナムするところです。 10年たったらまた読もう。

グローバル変数を横から覗く

この記事はカレーのち ぴょこりんクラスタ Advent Calendar 2018のために書いたものです。

サマリ

Linux/proc/${pid}/memを使って、グローバル変数を読み書きするものを作りました。 ptraceを使わなくても読み書きできます。使い方は以下のとおりで、WRITE_VALUEを省略すると リード、WRITE_VALUEを書くとライトします。1、2、4、8バイトの変数に対応できますが、 配列の途中とか構造体のメンバ変数はまだダメです。ELFバイナリを読むので、 C/C++などで作ったバイナリでないと動きません。

# ./gvtool.py </path/to/binary> <PID> <GLOBAL_VARIABLE_NAME> {<WRITE_VALUE>}

ソースコードはこちら。 github.com

動作はこんな感じです。

背景など

プログラムをチューニングする場合、パラメタをどこに置くかは結構悩ましい問題ですが、 ともかく手を抜くことを考えた場合、グローバル変数にえいやと書いてしまうことがあります*1

そのグローバル変数gdbなどのデバッガで書き換えながらいろいろ動作させるんですが、 デバッガを使うのが億劫なケースがたまにあります。例えば、他のプログラムと連動するようなものをチューニングする場合です。 デバッガを使う場合は、変数書き換え時に逐一プログラムをbreakする必要があるので、 タイミングによっては連動する他のプログラムが異常終了してしまうことがあります。 また、デバッガはインタラクティブに動かす必要があったりで、自動化がちょっとやりにくいというのもいまいちですね。

そういうちょっとした面倒さを解決するために、今回このgvtool.pyを作りました。 pet.py*2がELFを読むためのもの、gvtool.pyが/proc/${pid}/mapsを読んだり/proc/${pid}/memを読み書きするためのものです。

どうやって実現しているか

概要

要はメモリのある一部を書き換えるだけなので、メモリアドレス(=先頭アドレスとオフセット)さえわかればいいわけです。 つまり、シンプルに以下の3ステップでできます。

  • ELFバイナリのシンボルテーブルからアドレスのオフセットを求める
  • /proc/${pid}/mapsで先頭アドレスを調べる
  • 先頭アドレスとオフセットがわかったので、/proc/${pid}/memを読み書きする

ELF: Executable and Linkable Format

Linuxで採用している実行可能なバイナリのフォーマットです。 ELFには、シンボルテーブルと呼ばれる、変数の名前とメモリ空間上のオフセットを 保持しているセクションがありますので、そこを直接読みます。

シンボルテーブルのエントリのフォーマットは、 身近なところだと/usr/include/elf.hに載ってます。64bitだと以下のようになっていて、 ここのst_valueのところがアドレスオフセットになります*3

typedef struct
{
  Elf64_Word    st_name;        /* Symbol name (string tbl index) */
  unsigned char st_info;        /* Symbol type and binding */
  unsigned char st_other;       /* Symbol visibility */
  Elf64_Section st_shndx;       /* Section index */
  Elf64_Addr    st_value;       /* Symbol value */
  Elf64_Xword   st_size;        /* Symbol size */
} Elf64_Sym;

シンボルテーブルはデバッグ情報ではないので、-gをつけてコンパイルしていなくても読めます。

/proc/${pid}/maps

Linux Kernelが提供する、プロセスのメモリマップを教えてくれる疑似ファイルです。 例えばこんな感じで見えます。左から、

  • メモリの開始アドレス
  • メモリの終端アドレス
  • パーミッション
  • ファイルのオフセット
  • バイス番号
  • inode
  • ファイルパス

です。詳しくはここ

/home/bisco% cat /proc/$(pidof a.out)/maps
5591a7f3e000-5591a7f3f000 r--p 00000000 00:15 103135   /home/bisco/test/a.out
5591a7f3f000-5591a7f40000 r-xp 00001000 00:15 103135   /home/bisco/test/a.out
5591a7f40000-5591a7f41000 r--p 00002000 00:15 103135   /home/bisco/test/a.out
5591a7f41000-5591a7f42000 r--p 00002000 00:15 103135   /home/bisco/test/a.out
5591a7f42000-5591a7f43000 rw-p 00003000 00:15 103135   /home/bisco/test/a.out
5591a9395000-5591a93b6000 rw-p 00000000 00:00 0        [heap]
7f32b4401000-7f32b4423000 r--p 00000000 00:15 46396    /usr/lib/libc-2.28.so
7f32b4423000-7f32b456e000 r-xp 00022000 00:15 46396    /usr/lib/libc-2.28.so
7f32b456e000-7f32b45ba000 r--p 0016d000 00:15 46396    /usr/lib/libc-2.28.so
7f32b45ba000-7f32b45bb000 ---p 001b9000 00:15 46396    /usr/lib/libc-2.28.so
7f32b45bb000-7f32b45bf000 r--p 001b9000 00:15 46396    /usr/lib/libc-2.28.so
7f32b45bf000-7f32b45c1000 rw-p 001bd000 00:15 46396    /usr/lib/libc-2.28.so
7f32b45c1000-7f32b45c7000 rw-p 00000000 00:00 0
7f32b45cf000-7f32b45d1000 r--p 00000000 00:15 46385    /usr/lib/ld-2.28.so
7f32b45d1000-7f32b45f0000 r-xp 00002000 00:15 46385    /usr/lib/ld-2.28.so
7f32b45f0000-7f32b45f8000 r--p 00021000 00:15 46385    /usr/lib/ld-2.28.so
7f32b45f8000-7f32b45f9000 r--p 00028000 00:15 46385    /usr/lib/ld-2.28.so
7f32b45f9000-7f32b45fa000 rw-p 00029000 00:15 46385    /usr/lib/ld-2.28.so
7f32b45fa000-7f32b45fb000 rw-p 00000000 00:00 0
7fff149b7000-7fff149d8000 rw-p 00000000 00:00 0        [stack]
7fff149d8000-7fff149db000 r--p 00000000 00:00 0        [vvar]
7fff149db000-7fff149dd000 r-xp 00000000 00:00 0        [vdso]

グローバル変数は、heapでもstackでもなく、bssという領域にあります。 bssという領域は読み書きできるので、上から5番目の領域にいそうだな、ということが何となくわかりますね。

シンボルテーブルに載っているオフセットは、x番目の領域のオフセットではなく、先頭からのオフセットです。 a.outのグローバル変数にアクセスしたければ、a.outの先頭アドレス(例で言うところの0x5591a7f3e000)がわかればよいです。 この先頭アドレスに、 シンボルテーブルから得られたオフセットを足せばメモリアドレスがわかります。

/proc/${pid}/mem

Linux Kernelが提供する、プロセスのメモリ空間へアクセスするための疑似ファイルです。 setuid-rootなバイナリ(例えば実行者がrootとか)であれば、 プロセス${PID}をbreakすることなくメモリの読み書きが可能になります。 書き込みが可能になったのはKernel 2.6.38-rc8(2011/03/08)からであり、 昨今の大抵のディストリビューションで使えます。

以下、LKMLより抜粋。デバッグ用途で使ってくれとのことみたいですね。

  This patch series enables safe writes to /proc/pid/mem.  Such functionality is
  useful as it gives debuggers a simple and efficient mechanism to manipulate a
  process' address space.  Memory can be read and written using single calls to
  pread(2) and pwrite(2) instead of iteratively calling into *ptrace(2)* 
  In addition, /proc/pid/mem has always had write permissions enabled, so clearly it
  *wants* to be written to

まとめ

  • /proc/${pid}/mem/proc/${pid}/maps、シンボルテーブルの情報があれば、動いているプログラムのグローバル変数を読み書きできる

*1:そんなことはしないのが一番だと思いますが

*2:Python Elf Tool

*3:バイナリの種類によっては意味が違いますが、ここでは触れません

ゴルフを始めるときに買ったもの

この記事はまたまた!ぴょこりんクラスタAdvent Calendarのために書いたものです。

はじめに

今年からゴルフを始めた。結局最初に何が困るかと言うと、何を買えばよいか、である。この記事では自分が買ったもの、買えばよかったなと思ったものを書いておく。

買ったもの(最低限これだけあれば大丈夫)

  • ゴルフクラブセット
    • 安くてもいいのでひとそろい買う。何でもよいのでひとそろい持つことが重要。Amazonの最安とかでよい。
    • 材質が選べる場合、男性であればスチール製(重くしなりにくいので、ブン回しがちな男性向け)、女性であればカーボン製(軽い)がよさそう。男性がカーボン使ってもよいけど、しなるので打ちにくいかもしれない。
  • ゴルフボール
    • ロストボールが安く売ってるので、まずはそれを1ダースくらい買っておく。どうせ変なところに打ち込んでなくすので、量があるのが正義。
    • 黄色とかピンクとか派手なのがあって驚いたけど、実際派手なやつは目立ってわかりやすいしよい。
  • ゴルフシューズと専用袋
    • 紐で締めるものよりも、ダイヤルで締めるやつが圧倒的に楽なのでおすすめ。
    • スパイクなので、専用袋があったほうがよい。ビニール袋は破れちゃうから。
    • 歯が金属じゃないほうがいいらしいが、理由を聞き忘れた。
  • ゴルフ用ポロシャツ
    • ゴルフ用とは言うもののユニクロでも大勢に影響はない。
    • 真っ赤なやつとか派手なものでも全然気にならない。ゴルフファッションは現実と違うのだ。
  • ゴルフ用パンツ
    • ユニクロのチノパンでも見た目はまあ大丈夫だけど、動きやすさとか材質の違いとかあるので、ゴルフ用のほうがよさそう。
  • ゴルフ用帽子
    • サンバイザーがおしゃれ感あるけど、日除け兼ボール当たったとき用に帽子のほうがよい。
  • ティー
    • ボールを載せるやつ。長い(ドライバ用)のと短い(アイアン用)のがあるので、どちらも買う。これも安いやつでよい。すぐになくなったり折れたりするので、数を持っておく。

買ったもの(あるとよいもの)

  • 宅配便用ゴルフバッグカバー
    • ゴルフバッグをかついでゴルフ場に行くのは大変困難なので、宅配便を使って送る。その際にあると汚れたりしないし、伝票いれるところあるしで便利。
  • ゴルフ用レインコート
    • ゴルフは雨でもやるので、雨だとわかっているなら買っておく。専用品は蒸れにくいので、専用品を買うべき。一般品を買うと蒸し風呂のなかでプレイすることになってしまうらしい。

買ってないけどあるといいもの

  • 小物入れ
    • ゴルフボールとかティーを持ち歩く用。ポケットにいれてもよいが、個人的にポケットに物をあまりいれたくないので欲しい。

買わなくても何とかなるもの

  • グリーン用ボールマーカー
    • 他の人がパター打つ際、ボールが邪魔にならないようにどかすことがあるんだけど、そのときに自分のボールがどこにあったかをマークしておくもの。 たいていのゴルフ場に簡易なものがあるのでそれを使ってもよい。
    • 自分専用のものだとわかりやすいという利点はある。

おわりに

ものを揃えるのにお金がかかるけど、やってみると楽しいのでみんなやりましょう。

Google SpreadsheetにPythonから書き込んでみる

この記事はまたまた!ぴょこりんクラスタAdvent Calendarのために書いたものです。

はじめに

家計簿にZaimを使っているんだけど、いろいろ数字をこねたくなったので、Google Spreadsheetにデータを放り込みたい。 csvで落としてきて、それをGoogle SpreadsheetへAPIを使って書き込むようにしたときの話。

手順1: APIキーの発行

まずはAPIキーを発行しなければならない。意外に古い情報しかなくて、 このへんのを複数見ながらえいやでやった。大きな差分はないけど、 Google Drive APIではなくSpread sheet APIがあるのでそちらを選ぶようにする。 参考にしたのは以下の3つで、いちばん上のものが最も参考になった。

手順2:サンプルコードを動作させる

だいたいの野良サンプルにおいて、gspreadというPythonのライブラリを使っているけど、 更新が止まっていていまいちなので、本家のサンプルをまずは動かす。 Python2.6 or greaterとなっているので、何も考えずに従えばよい。

https://developers.google.com/sheets/quickstart/python

サンプルを何回か読むと意味がわかってくると思う。 何か指示するときはjsonを作ってPOST(多分)し、結果をjsonで得る、とそんな感じだ。

手順3:薄いラッパーをかぶせる

いくつかGoogle spreadsheet用Pythonライブラリがあるみたいだけど、 意味がわかってしまえばそんなに難しくはないので、自分でラッパーを作る。 直接APIを触ろうとすると、全体の流れがわかりにくくなるし、何より似たような定形文で 画面が埋まるのはよろしくない。

ラッパーと言っても大層なものではなく、渡したパラメタをjsonに埋めていく、とそんな程度のものである。 ラッパーは以下参照。サンプルコードをほとんど流用していることがわかると思う。 おそらくいちばん面倒であろう認証のところを隠してくれているので、そもそもやることがあまりないというのが 実情である。自分で作ったのはcreate_new_sheetメソッドとappend_dataだけ。 あまり隠せてないけれど、目的達成には十分。

gist.github.com

手順4:動かしてみる

動かしてみるとめちゃめちゃ遅い。数分くらい待たされるんだけど、これはいったい・・・?という感じで、 リアルタイムに使うのは厳しいかもしれない。APIキー登録のときに、どういうアプリケーションで使うのかと聞かれて、 daemon or cronみたいな項目をえらんだせいで後回しになっているのかもしれない。Web appを選んだらよかったかもね。

おわりに

google spreadsheetには書き込めるようになったけど、Zaimからのダウンロードが自動化できていないのでまだ微妙な感じ。 ただ、コピペよりはいくぶんましになったと思う。