There's an echo in my head

日々のメモ。

screen で reattach しても ssh-agent が効き続けるようにする。

仙石浩明の日記: ssh-agent を screen の中から使う方法 に書かれているように、 screen のなかで ssh-agent を使うにはちょっと一手間がいる。この中の環境変数 $SSH_AUTH_SOCK を毎回書き換える処理が、 OpenSSH 7.3 から導入された IdentityAgent を使うことで不要になっていたのでメモ。

なお、以下は全て screen を実行するリモートホスト側での作業になる。

まず ssh ログイン時に実行される ~/.ssh/rc で、 $SSH_AUTH_SOCK を元にシンボリックリンクを作る処理を入れる。これは従来と同じで、 ssh-agent のソケットファイルを同一のパスで指定できるようにするための対応である。

#!/bin/bash

# Fix SSH auth socket location so agent forwarding works within tmux/screen
if [ "$SSH_AUTH_SOCK" ]; then
  ln -sf $SSH_AUTH_SOCK ~/.ssh/agent.sock
fi

そして ~/.ssh/config で IdentityAgent にこのシンボリックリンクを指定する。

Host *
    IdentityAgent ~/.ssh/agent.sock

参考

macOS をセットアップするときに設定しているもの

OS のバージョンによって変わっているかもしれない。

:memo: Sonoma 以降に対応した記事を書いた。

確認ダイアログのボタンを Tab キーで選択できるようにする

Preferences -> Keyboard -> Shortcuts -> Use keyboard navigation to move focus between controls

スクロールバーを常に表示する

System Preferences -> General

自動補正をだいたい無効化する

System Preferences -> Keyboard -> Text

同一アプリケーション内のウィンドウを Option+Tab で切り替える

System Preferences -> Keyboard -> Shortcuts -> Keyboard

Terminal.app のタブを Ctrl+Tab で切り替える

System Preferences -> Keyboard -> Shortcuts -> App Shortcuts

Select~ と Show~ の両方を設定しているのは、バージョン間のメニュー名の差異によるものだろうか。

Terminal.app からコピーするときに装飾を除外する

$ defaults write com.apple.Terminal CopyAttributesProfile com.apple.Terminal.no-attributes

Terminal.app でコマンド実行した行に角カッコが付かないようにする

$ defaults write com.apple.Terminal AutoMarkPromptLines -bool NO

https://apple.stackexchange.com/questions/209635/what-functionality-do-marks-offer-in-the-el-capitan-terminal

タイトルバーのファイル名をダブルクリックしたときにファイル名の変更をしない

$ defaults write -g ApplePersistence -bool no

Google Chrome.app でスワイプしたときにナヴィゲーションの操作をしない

$ defaults write com.google.Chrome AppleEnableSwipeNavigateWithScrolls -bool FALSE

円マークでバックスラッシュを入力する

  1. Input method の設定画面を開く
  2. 日本語入力の ¥ の入力でバックスラッシュを入力するよう選択する
  3. あわせて Input modes でローマ字にチェックを入れる
  4. そうすると Input method で ABC の項目がマイナスボタンで削除できるようになるので、削除する

https://materialsinformaticsbeginner.blogspot.com/2021/03/backslash-setting-bigsur.html

【追記】C と Go で for 文のループでのスタックフレームの積み方が違うという気付き → 変数の確保の方法が違うだけだった

この記事を読んでいて、不思議に感じたのでメモ。

blog.p1ass.com

というのも、 低レイヤを知りたい人のためのCコンパイラ作成入門 を読みながら for 文を作っていたときにループごとにスタックフレームを新しく積むことはなかったので、別の変数に再代入しただけで結果が変わることに違和感を覚えた。もしかして C と Go で扱いが違うのかもと思って、ループ変数とループ内で宣言した変数のアドレスを書き出してみた。

C の場合はループ変数もループ内で宣言した変数もアドレスは毎回一致していた。ということは、毎回同じスタック領域をスタックフレームを使っていることになる。( for の初期化文などと同じフレームかまではわからないけど、 for 文のあとで i を参照することができないことを考えると、 for 文に入ったところで新しく積んでいるのかもしれない)

$ cat <<"EOF" >main.c
#include <stdio.h>

int main(int argc, char **argv) {
  for (int i = 0; i < 4; i++) {
    int j = i;
    printf("&i = %p, &j = %p\n", &i, &j);
  }

  return 0;
}
EOF
$ gcc main.c && ./a.out
&i = 0x7ffeec76353c, &j = 0x7ffeec763538
&i = 0x7ffeec76353c, &j = 0x7ffeec763538
&i = 0x7ffeec76353c, &j = 0x7ffeec763538
&i = 0x7ffeec76353c, &j = 0x7ffeec763538

Go の場合はループ変数のアドレスは同じだけど、ループ内で宣言した変数のアドレスは毎回変わっていた。ということは、ループ変数とは別に、ループの実行ごとにスタックフレームを追加で積んでいる…?

$ cat <<"EOF" > main.go
package main

import "fmt"

func main() {
    for i := 0; i < 4; i++ {
        j := i
        fmt.Printf("&i = %p, &j = %p\n", &i, &j)
    }
}
EOF
$ go run main.go
&i = 0xc00012a008, &j = 0xc00012a010
&i = 0xc00012a008, &j = 0xc00012a020
&i = 0xc00012a008, &j = 0xc00012a028
&i = 0xc00012a008, &j = 0xc00012a030

言語仕様や吐き出されたアセンブリなどを読めば厳密なことがわかりそうだけど、とりあえずここまで。

追記 2020-06-14

アセンブリを読んでみた。結論から書くと、

  • C の場合、ループごとにスタックフレームを確保する(rbpを積む)ことはなく、同じスタックを使っている
  • Go の場合もループの実行ごとにスタックフレームを追加で積んでいることはない。その代わり、変数の宣言のたびにメモリ領域を確保しているような雰囲気。

スタックフレームの積み方については、そうでなきゃループ前に宣言されたローカル変数が読めなくなっちゃうので当然か。

C

直接アセンブリを書き出す。

$ gcc -S -masm=intel -O0 main.c

結果はこの gist に貼った。 https://gist.github.com/a2ikm/04299c9d37c5b45edd1801825eec3041

メタデータっぽいものを削った main.s をみると、rbp を操作しているのは最初の1回だけなのでスタックフレームがループごとに積まれているということもない。 また、 printf の引数をみると &i に相当する -4[rbp]&j に相当する -8[rbp] が渡されていることから、毎回同じ変数を参照していることがわかる。

main:
.LFB0:
  push  rbp
  mov rbp, rsp
  sub rsp, 32
  mov DWORD PTR -20[rbp], edi
  mov QWORD PTR -32[rbp], rsi
  mov DWORD PTR -4[rbp], 0
  jmp .L2
.L3:
  mov eax, DWORD PTR -4[rbp]
  mov DWORD PTR -8[rbp], eax
  lea rdx, -8[rbp]
  lea rax, -4[rbp]
  mov rsi, rax
  lea rdi, .LC0[rip]
  mov eax, 0
  call  printf@PLT
  mov eax, DWORD PTR -4[rbp]
  add eax, 1
  mov DWORD PTR -4[rbp], eax
.L2:
  mov eax, DWORD PTR -4[rbp]
  cmp eax, 3
  jle .L3
  mov eax, 0
  leave
  ret

Go

アセンブルした。

$ go build -gcflags '-N -l' main.go
$ objdump -M intel -d main > main.s

-N-l はそれぞれ最適化とインライン化を行わないための go tool compile コマンドのオプション。また、 go tool compile -N -l-S main.go でも直接アセンブリを書き出すことができるようだけど、見慣れない AT&T syntax で読みづらかったのでこうした。

結果はこの gist に貼った。実行ファイル全体なのでデカい。 https://gist.github.com/a2ikm/bdd977b0381d0874bed89c313cd6ee1d

{パッケージ名}.{関数名} がそのまま記録されているので main 関数の位置はすぐわかる。

0000000000492fe0 <main.main>:

ここでも rbp の操作をしているのは最初の1回だけなので、ループごとにスタックフレームを追加していることは無い。

  493007: 48 8d ac 24 a0 00 00  lea    rbp,[rsp+0xa0]

変数 i を定義しているのはこの辺。cmp で4と比較しているあたりから目星がつけられる。

  49300f: 48 8d 05 ca e3 00 00  lea    rax,[rip+0xe3ca]        # 4a13e0 <type.*+0xd3e0>
  493016: 48 89 04 24           mov    QWORD PTR [rsp],rax
  49301a: e8 91 8b f7 ff        call   40bbb0 <runtime.newobject>
  49301f: 48 8b 44 24 08        mov    rax,QWORD PTR [rsp+0x8]
  493024: 48 89 44 24 60        mov    QWORD PTR [rsp+0x60],rax
  493029: 48 c7 00 00 00 00 00  mov    QWORD PTR [rax],0x0
  493030: eb 00                 jmp    493032 <main.main+0x52>
  493032: 48 8b 44 24 60        mov    rax,QWORD PTR [rsp+0x60]
  493037: 48 83 38 04           cmp    QWORD PTR [rax],0x4
  49303b: 7c 05                 jl     493042 <main.main+0x62>
  49303d: e9 07 01 00 00        jmp    493149 <main.main+0x169>

名前からして怪しそうな runtime.newobject を覗いてみる。

000000000040bbb0 <runtime.newobject>:
  40bbb0: 64 48 8b 0c 25 f8 ff  mov    rcx,QWORD PTR fs:0xfffffffffffffff8
  40bbb7: ff ff 
  40bbb9: 48 3b 61 10           cmp    rsp,QWORD PTR [rcx+0x10]
  40bbbd: 76 3d                 jbe    40bbfc <runtime.newobject+0x4c>
  40bbbf: 48 83 ec 28           sub    rsp,0x28
  40bbc3: 48 89 6c 24 20        mov    QWORD PTR [rsp+0x20],rbp
  40bbc8: 48 8d 6c 24 20        lea    rbp,[rsp+0x20]
  40bbcd: 48 8b 44 24 30        mov    rax,QWORD PTR [rsp+0x30]
  40bbd2: 48 8b 08              mov    rcx,QWORD PTR [rax]
  40bbd5: 48 89 0c 24           mov    QWORD PTR [rsp],rcx
  40bbd9: 48 89 44 24 08        mov    QWORD PTR [rsp+0x8],rax
  40bbde: c6 44 24 10 01        mov    BYTE PTR [rsp+0x10],0x1
  40bbe3: e8 78 f4 ff ff        call   40b060 <runtime.mallocgc>
  40bbe8: 48 8b 44 24 18        mov    rax,QWORD PTR [rsp+0x18]
  40bbed: 48 89 44 24 38        mov    QWORD PTR [rsp+0x38],rax
  40bbf2: 48 8b 6c 24 20        mov    rbp,QWORD PTR [rsp+0x20]
  40bbf7: 48 83 c4 28           add    rsp,0x28
  40bbfb: c3                    ret    
  40bbfc: e8 6f e1 04 00        call   459d70 <runtime.morestack_noctxt>
  40bc01: eb ad                 jmp    40bbb0 <runtime.newobject>

この中で呼ばれている runtime.mallocgc はまさに変数領域を確保するための関数。したがって、スタックが積まれるとかではなくて、 j := i で変数が宣言されるたびに領域が確保されているために毎回アドレスが変わっているのではないかと。

この関数名でググった結果、 https://medium.com/a-journey-with-go/go-memory-management-and-allocation-a7396d430f44 などが詳しそう。

Rails 5.x に更新する際に rails_kwargs_testing gem を使ったら便利だった

Rails 4.x で動いている社内ツールを Rails 5.x に更新する作業を最近やっていて、コントローラ・リクエスト周りのテストの書き換えに rails_kwargs_testing gem が便利だったのでメモ。

ざっくり書くと、これを使うと Rails 5.x 用のテストコードに寄せた状態で Rails 4.x 上でテストを実行できるようになるので、 BUNDLE_GEMFILERails 4.x と 5.x 用の Gemfile を指定することで両方を CI で走らせることが容易になる。詳細は作者である r7kamura さんのブログに書かれているので、そちらを参照してもらいたい。

r7kamura.com

rspec の場合の tips として、テストファイルごとの describe ブロックで prepend するのではなく、 controller spec や request spec で指定されている :type メタデータを使うことでまとめて prepend することができる。

# spec/rails_helper.rb や spec/supports/rails_kwargs_testing.rb みたいなファイル
if defined?(RailsKwargsTesting)
  RSpec.configure do |config|
    config.prepend RailsKwargsTesting::ControllerMethods, type: :controller
    config.prepend RailsKwargsTesting::RequestMethods, type: :request
  end
end

Minitest を使っている場合も、ちょっと手間だけど、 ActionController::TestCase を継承した共通のクラスを介することで同じようにまとめて対応することができる。こうしておけば、あとから rails_kwargs_testing gem を完全に消すことになったときにも手間が少ない。

なお、上記の記事で紹介されている rails5-spec-converter gem を使うとテストコードの一括書き換えのもできて便利。

middleman で netlify のPretty URLs 用に /_headers を生成する方法

Pretty URL (/blog/recent みたいなやつ)なページを生成してそのまま netlify にアップロードするだけだと、そのページにアクセスしたときに Content-Type: text/plain で返ってきてしまい、HTMLとしてレンダリングしてくれないという課題がある。これには、 /_headers というファイルでページごとのレスポンスヘッダを設定することで対応できる。

# /_headers
/blog/recent
    Content-Type: text/html

ただ、 middleman だと、名前が _ から始まるファイルは partial template として扱われてしまうのと、デフォルトだと拡張子無しのページを生成することができないのとで、この /_headers ファイルを生成するためにひと工夫必要になる。

具体的には次のように proxy を指定して適当なファイル(ここでは source/netlify_headers.txt.erb とする)を /_headers として生成するだけ。

# config.rb
proxy '/_headers', '/netlify_headers.txt', layout: false, ignore: true

ここで書いている ignore: true オプションは、 proxy 先の /netlify_headers.txt は生成しないことを指定している。

そして、 source/netlify_headers.txt.erb には前述したような /_headers に必要な記述をしておく。もしページ全てが対象なら sitemap を使って列挙すると良い。

<%# source/netlify_headers.txt.erb %>
<%- (sitemap.resources.map(&:url) - %w(/_headers)).each do |url| -%>
<%= url %>
  Content-Type: text/html
<%- end -%>

参考

Homecomings / PET MILK @恵比寿LIQUIDROOM

homecomings.jp

よかった…本当によかった…。

I WANT YOU BACKが一番好きです。

今回のタイトルになったというスチュアート・ダイベックの「シカゴ育ち」を読んでいるけど、街角の風景の一部を濃厚に切り取ったような短編で、こちらも良い。

セットリスト

  1. Songbirds
  2. Hull Down
  3. Parks
  4. PLAYYARD SYMPHONY
  5. Wonder Wander
  6. HURTS
  7. Smoke
  8. ANOTHER NEW YEAR
  9. So Far
  10. Moving Day Part1
  11. Drop
  12. WINTER BARGAIN
  13. LIVING LIFE (Daniel Johnston)
  14. WELCOME TO MY ROOM
  15. Continue
  16. Torch Song
  17. BUTTERSAND
  18. LIGHTS
  19. Blue Hour
  20. Cakes

アンコール

  1. GREAT ESCAPE
  2. Pedal
  3. I WANT YOU BACK

ダブルアンコール

  1. Whale Living

ハンバートハンバート / ハンバート家の平日 2019 Final

よかった…よかったんだけど、寝不足による夢うつつな状態で見てしまって勿体無いことした…。

家族連れで来ている人が多くて、そのためか途中休憩があった。

セットリスト

  1. ハイホー
  2. がんばれ兄ちゃん
  3. おじさんと酒
  4. まぶしい人
  5. 透明人間
  6. 鬼が来た
  7. バビロン
  8. ぶらんぶらん
  9. やさしい気持ち
  10. 長いこと待っていたんだ
  11. おなじ話
  12. ひなぎく
  13. 小さな声
  14. 邂逅
  15. ぼくのお日様
  16. 23時59分
  17. おいらの船
  18. ホンマツテントウムシ~ハイホー

アンコール

  1. Farewell song
  2. 喪に服す時

:memo: https://www.livefans.jp/events/1113702 より引用

このブログに出てくるコードスニペッツは、引用あるいは断りがない限りMITライセンスです。