ま、そんなところで。

ニッチな技術系メモとか、車輪を再発明してみたりとか.

肥大化してしまったGitリポジトリのパフォーマンス低下を軽減するTIPS

開発プロジェクトが長いとリポジトリが肥大化しがちです.
大きくなったリポジトリではcloneやcheckoutのパフォーマンスが重く、ストレスを感じることが多いと思います.
そこで、大きくなってしまったリポジトリとうまく付き合っていくためのTipsをまとめてみました.

1. 最新履歴のみをcloneする(shallow-clone)

通常のcloneではリポジトリ内のすべての履歴を取得しますが、 CIツールなど、最新コミットや一部の履歴のみあれば十分な場合に shallow-clone を使用すると、ダウンロードするリポジトリデータのサイズを抑えることができます.

1-1. shallow-cloneを行う

shallow-cloneを行うには、clone時に以下のオプションを指定します.

# デフォルトブランチで、履歴N個分をclone
$ git clone [url] --depth N
# 指定したブランチで、履歴N個分をclone
$ git clone [url] --single-branch -b [ブランチ名] --depth N
# 全てのブランチについて、履歴N個分をclone
$ git clone [url] --no-single-branch --depth N

1-2. shallow-cloneされたブランチのupdate

shallow-cloneしたリポジトリのブランチをupdateするには、update-shallow オプションを指定します.
shallow-cloneで取得した起点コミット(grafted)以降の履歴のみを取得することができます.

# 指定ブランチについて現在のコミットより新しい履歴を取得
git fetch --update-shallow [remote名] [ブランチ名]

ブランチ名を省略することですべてのブランチについてshallow-clone起点コミット(grafted)以降の履歴を取得することもできます.
ただし、shallow-cloneが維持されるのは既存のブランチのみで、 ローカルに起点コミットがない(ex. リモートに新規ブランチがある)場合は、新規ブランチについてすべての履歴が取得されてしまいます.
これを防ぐには depth オプションを指定して、新規含むすべての最新コミットを起点コミットとして取得しなおします.

# すべてのブランチについて最新履歴のみ取得した状態にする
$ git fetch --update-shallow [--depth=N]

1-3. shallow-cloneの解除

shallow-cloneを解除するには、unshallow オプションをつけてfetchをおこないます.

$ git fetch --unshallow

2. 必要なオブジェクトのみをcloneする(partial-clone)

リポジトリをcloneする際に、Gitの履歴オブジェクトのうち、実データ(blob)やファイル構成(tree)に関する情報は、実際に必要になったタイミングで行うよう設定します.
遅延取得するデータの種類によってはgitの一部機能に制限がかかることがありますが、初回clone時にダウンロードするローカルリポジトリのデータサイズを抑えることができます.

前提として、bareリポジトリのあるサーバ側のgit設定として下記が必要になります.

# bareリポジトリ側にfilterを許可する設定
# ※ --globalをつけてサーバ側の実行ユーザに設定してもOK.
$ git config uploadpack.allowfilter true
$ git config uploadpack.allowanysha1inwant true

2-1. partial-cloneの種類

cloneから除外するオブジェクトの種類によって以下の2種類があります.

(1) tree-less clone

最新コミットと履歴のみを初回cloneで取得.

(2) blob-less clone

最新コミットと履歴のtreeを取得.

partiah-cloneについては、以下のサイトの解説がわかりやすいです.
github.blog

2-2. partial-cloneの操作

partial-cloneリポジトリは、以下のように設定します.

2-2-1. 新規にcloneするとき

# tree-less clone
$ git clone --fliter=tree:none [url] [clone_to]

# blob-less clone
$ git clone --fliter=blob:none [url] [clone_to]

2-2-2. completeリポジトリからpartial-cloneリポジトリへの変換

既存のリポジトリをblob-lessに変換する例.

# blob-less clone リポジトリへのconvert
# 下記の設定を追加
$ git config remote.[remote_name].promisor  true
$ git config remote.[remote_name].partialclonefilter  blob:none
# 不要なlocalオブジェクトを破棄. (git のバージョンに注意. filter-toは最近追加されたもの)
# filter-toには除外ファイルのprefixを指定する. 
# ./hoge/fugaのようにファイルパスでprefixを指定すれば、別ディレクトリへの出力も可能.
$ git repack -ad --filter=blob:none --filter-to=pack --no-write-bitmap-index
# 除外したオブジェクトのファイルを削除
$ rm -f pack-*

2-2-3. partial-cloneリポジトリからcompleteリポジトリへの変換

# partial-cloneのフィルタ設定を削除
$ git config --unset remote.[remote_name].partialclonefilter
# リポジトリデータの再fetch
$ git fetch --refetch
# (任意) refetchの結果、リポジトリサイズが通常のcomplete-cloneリポジトリよりも大きくなることがある.  
# 重複した情報のオブジェクトをpackから削除して整理する
$ git gc --aggressive

3. 必要なファイルのみをcheckoutする(sparse-checkout)

submoduleで他プロジェクトを参照するときなどは、プロジェクトの一部ファイルのみさえあればよいといった状況があります.
sparse-checkoutでは、作業領域に展開するファイルを指定したファイル/ディレクトリのみに限定します.
ブランチを切り替えるときに、すべてのファイルをローカルに再現する必要がなくなるので、checkoutによるブランチ切り替えが高速になります.

3-1. sparse-checkoutを設定する

コマンドで設定する場合は以下の手順を行います.

# sparse-checkoutの適用(.git/info/sparse-checkout の新規作成)
$ git sparse-checkout init
# chekcout対象を限定する
# hogeディレクトリとfugaディレクトリに限定
$ git sparse-checkout set hoge/ fuga/
# 対象の追加など
$ git sparse-checkout add hoga/

.git/info/sparse-checkoutファイルを直接作成/編集しても設定ができます.
.gitignoreと同じ書式で対象ディレクトリ/ファイルを指定します.

# ## sparse-checkoutファイルの例 ## 
# root直下のファイルをcheckout
/*
# 先頭に!をつけるとNot条件になります
# root直下にあるディレクトリはcheckoutしない
!/*/
# root直下にあるhogeディレクトリ以下はcheckoutする
/hoge/
# root直下にあるfugaディレクトリ以下はcheckoutする
/fuga/

ファイル単位で指定する場合は、.git/info/sparse-checkout ファイルを直接編集してから git read-treeで適用します.

# sparse-checkoutファイルを編集してから実施.  
# sparse-checkoutファイルの編集内容を作業領域に反映. 指定ファイルのみcheckoutされた状態にする.
$ git read-tree -mu HEAD

3-2. sparse-checkoutの設定解除

以下コマンドを実行するとsparse-checkoutが解除できます.

# sparse-checkoutの適用解除
$ git sparse-checkout disable

3-3. sparse-checkoutの再設定

info/sparse-checkoutの内容は残っているので、ファイルの内容を再設定します.

# sparse-checkoutの再設定
$ git sparse-checkout reapply

参考情報

Debug構成以外でもライブビジュアルツリーからXamlへのコードジャンプを有効にする

Live Visual Tree のコードジャンプ

VisualStudioのデバッグツールには実行中のWPFアプリのUIコンポーネントをツリー形式で表示する ライブビジュアルツリーという機能があります.
この機能、すべての構成で表示はできるのですが、コンポーネント横に表示されるアイコンをクリックして対応しているXamlファイルへジャンプする機能は Debug構成のみでしか有効になっていません。

しかし、Debug構成以外であってもUI構成を解析するなど使用したいことがあります.

Debug構成以外でXamlへのコードジャンプを有効にする

ライブビジュアルツリーウィンドウから Xaml ファイルへのコードジャンプを有効にする設定は、 Visual Studio上からは行えないようで、csprj ファイルを直接編集します.

(1) デスクトップアプリのプロジェクト

デスクトップアプリのプロジェクトの場合は、有効にしたいビルド構成の PropertyGroup 以下に XamlDebuggingInformation 要素を挿入して、値をTrueにします.

  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'RelWithDebInfo|x64'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>$(SolutionDir)x64\$(Configuration)\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <Optimize>false</Optimize>
    <DebugType>full</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <UseVSHostingProcess>true</UseVSHostingProcess>
    <!-- この要素を挿入 -->
    <XamlDebuggingInformation>true</XamlDebuggingInformation>
    <!-- この要素を挿入 -->
    <ErrorReport>prompt</ErrorReport>
    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
    <Prefer32Bit>true</Prefer32Bit>
  </PropertyGroup>

(2) UWPアプリのプロジェクトの場合

UWPアプリの場合は、DisableXbfLineInfo 要素を挿入して、値をFalseにします.

参考リンク

Troubleshooting XAML Hot Reload - Visual Studio (Windows) | Microsoft Learn

[メモ] リファレンスブックマーク

リファレンスブックマーク集

PortableなLinuxバイナリの作り方

$ORIGINについての解説あり nehckl0.medium.com

RunLevelの変更

永続的なrunlevelの変更

www.server-world.info

一時的にrunlevelを落として起動するための処置.

www.if-not-true-then-false.com

xtech.nikkei.com

systemctrl を使った変更 atmarkit.itmedia.co.jp

仮想マシン向け I/O schedulerの変更

I/Oスケジューラの設定を一時的に変更

LinuxでのディスクI/Oスケジューラの検証

udevのrulesファイルを使用してschedulerの変更を自動化. manual.sakura.ad.jp

GRUB2で一時的にRunLevelを変更して起動する

一時的にRunlevelを変更して起動したい.

Guiに何らかの問題があり正しくdesktopモード(runlevel5)Bootしなくなったときなどに 一時的にrunlevelを落として起動するための処置.

www.if-not-true-then-false.com

linuxのある行の最後にrunlevelを数値指定すれば良いようです.
この変更は一時的で、次回Boot時にはもとに戻るようです.

Ubuntu 22.04.1 ある日突然 Dummy Output になった問題

ある日突然音声出力が Dummy Output に

apt full-upgradeしたら 突然Soundが Dummy Output となって音声がでなくなる現象に遭遇しました.
PluseAudio絡みの問題かと思い、色々調べてみますが一向に解決せず。

askubuntu.com

apt upgradeで誤ったカーネルがインストールされてしまっていた

よくよく調べてみると、音声がでなくなるだけでなくいろいろ起動しなくなっている様子。

  • ディスプレイが自動でOFFにならない
  • VirtualBoxがエラーで起動しない

これはPluseAudio絡みではなくカーネルの問題ではないか・・・?、とカーネルのバージョンを調べたら・・

ん??

$ uname -r 
5.15.0-1025-oracle

なんて知らないバージョンが勝手にinstallされて使われてました.
どうやら最近のupdateで起こったバグのようです.

askubuntu.com

ネットワークなんかも動かなくなっているケースがあるようですね..

issueも上がっていました.

bugs.launchpad.net

ということでGrub2で正しいカーネルを指定して再起動し、知らないバージョンのカーネルイメージを削除して無事解決しました.

$ sudo apt remove linux-image-5.15.0-1021-intel-iotg linux-image-5.15.0-1025-oracle linux-modules-5.15.0-1021-intel-iotg linux-modules-5.15.0-1025-oracle linux-modules-5.15.0-56-lowlatency linux-image-5.15.0-56-lowlatency

参考サイト

VirtualBox の Guest OS 上でビルドすると internal compiler error が多発する現象 (Ubuntu 22.04.1)

Guest OS 上でビルド中 internal compiler error が発生してビルドできない・・・

ある日突然仮想マシン上の Guest OSでビルドすると internal compiler error が多発する現象に遭遇.

apportのログを見ると、ビルド中に Segmentation Fault が発生してビルドが中断している模様.
しかし、コードに問題があるわけではなく発生箇所は毎回変化し、ビルドを再開すると、しばらくビルドが進んでまた internal compiler error が発生.

これ、 通常であればコンパイラバグ、ハードウェアの問題かカーネルバグを疑うべきところなんですが・・・

  • 今回は、エラーが毎回違う場所で発生しているので、コンパイラのバグの可能性は薄い.
  • Guest OS でのみ発生しており、Host OS 側は何の問題もありませんから、マシンのハードウェアの可能性は薄い.
  • Guest OS のメモリ不足も疑うべきですが、Guest OS へのメモリ割当ては16Gと十分でこれも可能性は薄い.

となると・・残るはVirtualBoxが怪しいということに・・

Ubuntu公式リポジトリVirtualBoxとホスト側カーネルの不整合が原因

VirtualBoxUbuntu 22.04.1 で似たような問題が起きてないか漁っていると・・・
以下に同じ現象がpostされているのを発見.

askubuntu.com

この現象、 ホスト側カーネルlinux-image-5.15.0-47-generic だと発生し、一つ前のカーネル linux-image-5.15.0-46-generic では発生しないそうで.
カーネルバージョンとvirtualboxのバージョンの組み合わせで発生する問題なのだとか・・・.

公式リポジトリのパッケージの組み合わせで発生しちゃうので、apt-get dist-update (apt full-upgrade) したらある日突然に、新規で環境を作る場合はいきなりこの現象に遭遇することになるわけです.
これは結構・・心臓に悪いですね・・・.

公式リポジトリVirtualBoxOracle提供のものに変更すれば解決.

解決にはOracleが提供するVirtualBoxの最新版に変更すればよいようです.

Ubuntu公式リポジトリ由来の virtualbox と関連パッケージをすべて削除して、Oracle版のもののみをインストールします.
aptの依存関係による自動削除に任せるとUbuntu公式由来の依存パッケージが残ってしまうことがあるので、packageの依存関係を確認して、削除するパッケージを各々個別に指定して確実に削除する必要があります .
Ubuntu公式リポジトリ由来のパッケージが残っていると、ゲストOS起動時にエラーになったりCrashしたりしますので注意.

# Oracleのリポジトリと公開鍵を追加
$ sudo echo "deb [arch=amd64 signed-by=/usr/share/keyrings/oracle-virtualbox-2016.gpg] https://download.virtualbox.org/virtualbox/debian jammy contrib" > /etc/apt/sources.list.d/virtualbox.list
$ wget -O- https://www.virtualbox.org/download/oracle_vbox_2016.asc | sudo gpg --dearmor --yes --output /usr/share/keyrings/oracle-virtualbox-2016.gpg
# パッケージ情報更新
$ sudo apt update

# 公式リポジトリ由来のvirtualboxと関連パッケージを明示的に削除.
$ sudo apt purge virtualbox virtualbox-dkms virtualbox-ext-pack virtualbox-qt
# Oracleのvirtualboxをインストール
$ sudo apt install virtualbox-6.1

Oracle版の VirtualBox を立ち上げて Guest OS が問題なく起動できれば無事完了です.
これで、ビルド時に internal compiler error が頻発する現象も解消されました.

参考サイト

These articles have been very helpful for me. thanks!!

埋め込みpythonでのみモジュールのModuleLoadErrorが起こる問題

埋め込みpythonでのみモジュールがModuleLoadErrorを起こす!?

C++アプリケーションへpythonコードを埋め込んで使用していると、pythonコード内でimportしているモジュールの中に、ModuleLoadErrorを起こして正しく動作しなくなるものがあります.
debugpyも例外ではなく、エラーが出てC++アプリへ組み込んだ状態でのpythonコードのデバッグができません・・・

しかし、問題を起こすpythonコードをpythonインタプリタから単独で実行したときは何の問題もなく動作するんですよね.
これはハマりました.

pythonモジュール側のシンボルが解決できないのが原因

どうも _ctypes が存在しないとかなので、pythonランタイム側のシンボルが見えていないようですが・・・

原因の可能性の一つは、libffi問題.
zv-louis.hatenablog.com
こちらは過去の記事にて解決済み.
事実、python インタプリタで実行した場合は動作しています.
明らかに別の問題です・・・

色々探してると、この問題に関する記事がありました.

stackoverflow.com

C/C++の拡張モジュールが、pythonモジュール pythonXX.so 内のシンボルを参照している場合に、実行時にシンボル参照を解決できないと発生する問題、とのこと.

いわれてみれば、現象が発生するモジュールはすべてC/C++拡張タイプのモジュールであるという特徴があります.
これは盲点でした.

確かに、C/C++拡張モジュールを作るとき、pythonXX.so を参照させるようにする必要がありました.
当然、実行時のシンボル解決には pythonXX.so のシンボルが見えていなくてはなりません.
ところが、拡張モジュールはどこにデプロイされてどこからロードされるかが不定なライブラリですから、事前にシンボル解決で参照する依存先をパスで指定することなどはできず、シンボル解決はもっぱら 参照可能なロード済みシンボル に依存せざるを得ないのでした.

組み込み先のシンボルを可視化する

--export-dynamicを指定してリンクする

pythonコードを組み込むモジュールのリンク時にリンカオプション -export-dynamic 指定をする.
これは、ロード済みシンボルを後でロードするモジュールのシンボル解決に使用可能にするオプション.

この件については、以下にリンカオプション例が示されています.
ちゃんと読まなきゃダメですね.

docs.python.org

python組み込み先が共有ライブラリの場合の注意点

pythonコードを組み込んだモジュールが共有ライブラリの場合、別の問題があります.

組み込んだ共有ライブラリをロードするときにdlopenを使う場合、--export-dynamic のリンク指定は無視されて、dlopenのロードオプションが適用されます.
ほとんどの実行環境では dlopen の RTLD_LAZY, RTLD_LOCAL いずれかがデフォルトになっているために、pythonモジュールがロードされるタイミングで必要なシンボルがすでにロードされて参照可能となっておらず、モジュールで必要なシンボルを解決できないことがあるので注意が必要です.

確実にロード済みシンボルを参照できるようにするには、 dlopenを使用するときに RTLD_NOWRTLD_GLOBAL オプションを明示的に指定する必要があります.

    // hoge.soへpythonXX.soをリンクして組み込んでいる場合
    void* hmod = nullptr;
    std::string mod_path("hoge.so");
    hmod = dlopen(mod_path.c_str(), RTLD_NOW | RTLD_GLOBAL);

リファレンス