プロセスのダンプファイルを取得してデバッグする方法

プロセスのダンプファイルを取得してデバッグする方法

たとえばハングしているプロセスのダンプファイルを取得して別の環境で解析する方法について記述する。

Windowsの場合

ダンプファイルの取得方法

タスクマネージャーからプロセスを選択してダンプファイルを作成する。

しばらくすると、一時フォルダにDMPファイルが作成される。

ダンプファイルをVisualStudioでデバッグする方法

VisualStudioを使用することでDMPファイルを開いてデバッグを行える。
.NETFrameworkを使用している場合

.NETFrameworkを使用していない場合

.NETFrameworkを使用している場合は「マネージドのみでデバッグ」または「混合でデバッグ」を選択する。
.NETFrameworkを使用していない場合は「ネイティブのみでデバッグ」を選択する。

「スレッド」ウィンドウにはプロセスのスレッドの一覧が表示される。
「呼び出し履歴」ウィンドウには選択したスレッドの呼び出し履歴が表示される。
呼び出し履歴を選択することで、その箇所のコードが表示される。
これらを利用することでデッドロックしている箇所をしらべることができる。

GDBが使える環境の場合

ダンプファイルの取得方法

gdbを起動して以下のコマンドを実行する。

attach プロセスID
gcore 出力ファイル名
detach
quit

ダンプファイルをgdbでデバッグする方法

起動方法

gdb 実行ファイル名 ダンプファイル

スレッドの一覧を表示する方法
Debugging programs with multiple threads

(gdb) info threads
  Id   Target Id         Frame
  6    Thread 0x7f24db490740 (LWP 11479) 0x00007f24da859f47 in pthread_join ()
   from /lib64/libpthread.so.0
  5    Thread 0x7f24d847f700 (LWP 11484) 0x00007f24da548e2d in nanosleep ()
   from /lib64/libc.so.6
  4    Thread 0x7f24d8c80700 (LWP 11483) 0x00007f24da548e2d in nanosleep ()
   from /lib64/libc.so.6
  3    Thread 0x7f24d9481700 (LWP 11482) 0x00007f24da548e2d in nanosleep ()
   from /lib64/libc.so.6
  2    Thread 0x7f24d9c82700 (LWP 11481) 0x00007f24da548e2d in nanosleep ()
   from /lib64/libc.so.6
* 1    Thread 0x7f24da483700 (LWP 11480) 0x00007f24da548e2d in nanosleep ()
   from /lib64/libc.so.6

スレッドの切り替え方法

(gdb) thread 5
[Switching to thread 5 (Thread 0x7f24d847f700 (LWP 11484))]

呼び出し履歴の表示
backtraceコマンドを使用すると現在のスレッドの呼び出し履歴が確認できる。

(gdb) bt
#0  0x00007f24da548e2d in nanosleep () from /lib64/libc.so.6
#1  0x00007f24da579704 in usleep () from /lib64/libc.so.6
#2  0x00000000004012e6 in f(int) ()
#3  0x00000000004029f0 in void std::_Bind_simple<void (*(int))(int)>::_M_invoke<0ul>(std::_Index_tuple<0ul>) ()
#4  0x00000000004028fd in std::_Bind_simple<void (*(int))(int)>::operator()()
    ()
#5  0x0000000000402896 in std::thread::_Impl<std::_Bind_simple<void (*(int))(int)> >::_M_run() ()
#6  0x00007f24db03a070 in ?? () from /lib64/libstdc++.so.6
#7  0x00007f24da858dd5 in start_thread () from /lib64/libpthread.so.0
#8  0x00007f24da581ead in clone () from /lib64/libc.so.6

Javaの場合

ヒープダンプを作成する

jmapコマンドでヒープダンプを作成することが可能。

jmap -dump:format=b,file=<file-path> <pid> 

作成したヒープダンプからメモリの使用状況を調べることができる。
ヒープダンプを解析するにはjvisualvmなどを利用する。

スレッドダンプを作成する

jstackコマンドを使用するとスレッドダンプが作成することが可能。

jstack -l <pid> 

現時点のスレッドの情報を取得することができ、デッドロックをしていると以下のようになる。


Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x0000000003aa8138 (object 0x00000007018084d8, a ChopSticks),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x0000000003aa8088 (object 0x0000000701800bf0, a ChopSticks),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
        at Human.eat(Human.java:35)
        - waiting to lock <0x00000007018084d8> (a ChopSticks)
        - locked <0x0000000701800bf0> (a ChopSticks)
        at Human.run(Human.java:18)
"Thread-0":
        at Human.eat(Human.java:35)
        - waiting to lock <0x0000000701800bf0> (a ChopSticks)
        - locked <0x00000007018084d8> (a ChopSticks)
        at Human.run(Human.java:18)

Found 1 deadlock.

コアダンプからヒープダンプとスレッドダンプを作成する

タスクマネージャーで作成したjavaのコアダンプはjvisualvmで解析することができる。
jvisualvmを起動してVMのコアダンプからjavaのダンプファイルを開いてヒープダンプやスレッドダンプを作成可能になっている。

jvisualvmで起動中のプロセスからヒープダンプやスレッドダンプを作成する

jvisualvmは起動中のプロセスから直接スレッドダンプやヒープダンプを作成することができる。

Visual Studio 2019でリモートデバッグを行う

Visual Studio 2019でリモートデバッグを行う

開発環境の入っていない端末でデバッグを行う場合、リモートデバッグを使用することができる。

以下参照
https://docs.microsoft.com/ja-jp/visualstudio/debugger/remote-debugging?view=vs-2019

クライアントのセットアップ

クライアントにリモート ツールをインストールする。

クライアントでRemote Debuggerを起動する

「Configure remote debugging」ボタンを押下することで、外部PCからのデバッグを受け付ける準備が整う。

VisaulStudioでデバッグする方法

Visual Studioからクライアントのプロセスにアタッチしてデバッグする方法

Visual Studioの「デバッグ」メニューからを選択して「プロセスにアタッチ」を選択する。

接続先の検索コンボボックスで接続先のPCを選択するか、検索ボタンを押下する。

デバッグをしたいプロセスが動作しているクライアントを選択する。

クライアントのプロセス一覧が表示されるのでデバッグ対象のプロセスを選択する。

C#のプロジェクトから起動してデバッグする方法

(1) Visual Studioのプロジェクトで作成したDebugビルドのEXEをクライアントにコピーする。この際、パスはクライアント側とVisualStudio側で合わせること

(2)プロジェクトのプロパティを開いて、開始オプションを変更する。

・「リモートコンピュータを使用する」にチェックを入れる。
・コンピュータ名とポート番号を入力する。
「コンピュータ名:番号」

(3)VisualStudioからデバッグを開始するとクライアント端末でデバッグ対象のプロセスが起動する。

https://docs.microsoft.com/ja-jp/visualstudio/debugger/remote-debugging?view=vs-2019

C++のプロジェクトから起動してデバッグする方法

(1)プロジェクトのプロパティを開いて、デバッグの設定を行う。

項目 設定値
起動するデバッガー リモート Windowsデバッガー
リモートコマンド クライアントで実行するEXEのパス
作業ディレクトリ プロセス起動時の作業フォルダ
リモートサーバー名 クライアントのPC名
配置ディレクトリ クライアントでEXEファイルを配置するフォルダ

(2)構成マネージャーで配置にチェックを付ける

(3)デバッグを開始すると、クライアント上の配置フォルダにEXEを配置してプロセスを開始してデバッグが可能になる。
なお、C++のプロセスを実行する場合、クライアントに再配布用のDLLをインストールだけでは動作しないので、VisualStudioのあるマシンからデバッグ用のDLLをコピーしておく必要がある。単純なMFCを動かす場合は以下のDLLが必要である。

  • mfc140ud.dll
  • ucrtbased.dll
  • vcruntime140_1d.dll
  • vcruntime140d.dll

https://docs.microsoft.com/ja-jp/visualstudio/debugger/remote-debugging-csharp?view=vs-2019

デスマーチの概念を初めて提唱した人の取り扱い

はじめに

デスマーチについてのWikipediaの記述が凄く引っかかり、また、多くの人がそれを無批判に受け入れているので記事を書きます。

2020/06/07時点のWikipediaより

https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%B3%E3%83%89%E3%83%AA%E3%83%A5%E3%83%BC%E3%83%BB%E3%82%B1%E3%83%BC%E3%83%8B%E3%83%83%E3%83%92

アンドリュー・ケーニッヒ (プログラマー)(英語版) - アメリカ合衆国のプログラマー。デスマーチの概念を初めて提唱した。

https://ja.wikipedia.org/wiki/%E3%83%87%E3%82%B9%E3%83%9E%E3%83%BC%E3%83%81

デスマーチ (death march) とは、プロジェクトにおいて過酷な労働状況をいう。本来は、コンピュータプログラマのアンドリュー・ケーニッヒ(英語版)によって1995年に示された、コンピュータシステムのアンチパターンのうち、プロジェクトマネジメント上の問題点の1つとして示した言葉である。

アンドリュー・ケーニッヒとアンチパターン

アンドリュー・ケーニッヒは英語版のWikiPediaによると「アンチパターン」で知られるとあります。
https://en.wikipedia.org/wiki/Andrew_Koenig_(programmer)

またこのページによると以下の経緯があるようです。

1995年 アンドリュー・ケーニッヒは、最初にJournal of Object Oriented Programの1995年3月-4月号でアンチパターンという用語を造語しました。"アンチパターンはパターンのようなものであり、解決策の代わりにそれが解決策のように表面的に見える何かを与えるが、1つではないことを除いて、ちょうどである"。

1998 Linda Risingは、アンドリュー・ケーニッヒのアンチパターンの定義をThe patterns handbook: techniques, strategies, and applicationsの中で再掲しています。

1998年 書籍「 AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis」という本は、この用語を広めました。

AntiPatterns: Refactoring Software, Architectures, and Projects in Crisisは日本では以下の書籍として出版されています。

アンチパターン―ソフトウェア危篤患者の救出
https://www.amazon.co.jp/dp/4797307587

英語版 Anti Pattern のWikiPediaの記載

2020/06/07時点のWikipedia
https://en.wikipedia.org/wiki/Anti-pattern

An anti-pattern is a common response to a recurring problem that is usually ineffective and risks being highly counterproductive. The term, coined in 1995 by Andrew Koenig, was inspired by a book, Design Patterns, which highlights a number of design patterns in software development that its authors considered to be highly reliable and effective.

The term was popularized three years later by the book AntiPatterns, which extended its use beyond the field of software design to refer informally to any commonly reinvented but bad solution to a problem. Examples include analysis paralysis, cargo cult programming, death march, groupthink and vendor lock-in.

1995年にアンドリュー・ケーニッヒが「anti-pattern」という言葉を造語し、3年後に書籍によって有名になったという記述は同じです。また、書籍化を行う際に、ソフトウェア設計を超えた分野についての問題を取り扱うようになり、そのなかにデスマーチも入っていると読み取れます。

アンチパターン―ソフトウェア危篤患者の救出におけるデスマーチの話

p258,259でデスマーチの話題をとりあつかっていますが、そこにアンドリュー・ケーニッヒは登場しません。
デスマーチの定義についてはエドワードヨードンの定義を参照しています。

エドワードヨードンのデスマーチの定義

エドワードヨードンは書籍デスマーチにおいて自身の考えるデスマーチの定義を行っています。

https://www.amazon.co.jp/dp/4810189821
https://www.amazon.co.jp/dp/4822282716

なお、第一版、第二版ともにアンチパターンについても、アンドリュー・ケーニッヒにも言及はありません。

まとめ

・アンドリュー・ケーニッヒはアンチパターンという言葉を提唱した。
・アンチパターンにデスマーチなどのソフトウェアの設計の範囲のものが追加されたのは書籍出版のタイミングと思われる。
・書籍のデスマーチの項目にはエドワードヨードンのデスマーチの定義が参照されている。

つまり、今、日本語Wikipediaで言われている「アンドリュー・ケーニッヒ がデスマーチの概念を初めて提唱した。」という記述には疑義がある。

これを確認するにはJournal of Object Oriented Programの1995年3月-4月号を読む必要がある。

PowerShellでVisualStudioを操作する

PowerShellでVisualStudioを操作する

VisualStudioをスクリプトで操作したい場合がたまにあります。たとえば、ソースコード中の関数の一覧を列挙したり、コードを自動生成する場合などです。

現VisualStudioのマクロの制限

以下の記事で紹介している復活したVisualStudioのマクロはJScriptからVisualStudio.DTEを利用して、VisualStudioを操作しています。

復活のVisualStudioのマクロ
https://qiita.com/mima_ita/items/c56d6e2657cc836af7ac

じつは旧マクロで実行できたことが、新しいマクロでは実行できないケースがあります。たとえばCodeElementで取得した関数のパラメータの列挙はかってのVisualStudioのマクロでは簡単におこなえましたが、新VisualStudioのマクロでは実行できません。

これはJScriptではインターフェイスの明示がおこなえないためと考えられます。

C#からのVisualStudioの操作

C#からのVisualStudioの操作は容易に行えます。
これを行うには参照の追加からアセンブリ→拡張をえらびEnvDTEXXXを追加する必要があります。

EnvDTEのバージョンはわかりにくいですが、EnvDTE80はEnvDTEに足りない機能を拡張したもの、EnvDTE90はEvnDTE+EnvDTE80に足りないものを拡張したものとなっています。つまり、EnvDTEを参照したあとに必要に応じて拡張されたものを参照することになります。

以下のコードは起動しているVisualStudio2019のソリューションから関数の一覧を表示するサンプルです。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace VSTest
{
    class Program
    {
        static void EnumCodeElements(EnvDTE.CodeElements elems)
        {
            foreach (EnvDTE.CodeElement elem in elems)
            {
                if (elem.Kind == EnvDTE.vsCMElement.vsCMElementFunction)
                {
                    EnvDTE.CodeFunction func = elem as EnvDTE.CodeFunction;
                    Console.WriteLine("{0} {1}", func.Type.AsFullName, func.FullName);
                    foreach(EnvDTE.CodeParameter param in func.Parameters)
                    {
                        Console.WriteLine("  {0} {1}", param.Type.AsFullName, param.FullName);
                    }
                }
                EnumCodeElements(elem.Children);
            }

        }

        static void EnumProjectItems(EnvDTE.ProjectItems items)
        {
            foreach (EnvDTE.ProjectItem item in items)
            {
                Console.WriteLine(item.Name);
                EnumProjectItems(item.ProjectItems);
                if (item.FileCodeModel != null)
                {
                    EnumCodeElements(item.FileCodeModel.CodeElements);
                }
            }

        }

        static void Main(string[] args)
        {
            EnvDTE.DTE dte = (EnvDTE.DTE)Marshal.GetActiveObject("VisualStudio.DTE.16.0");
            foreach (EnvDTE.Project prj in dte.Solution.Projects)
            {
                Console.WriteLine(prj.Name);
                EnumProjectItems(prj.ProjectItems);
            }

        }

    }
}

注意すべき点として、dynamicなどを使用してインターフェイスを明示しない場合、正常に動作しない箇所があるということに注意してください。

PowerShellからのVisualStudioの操作

さてPowerShellから同様の操作した場合、インターフェイスを明示するところで悩むことになります。
これはCOM経由でDTEを操作しているため、PowerShell上ではSystem.__ComObject型として扱われるため、適切なDTEのインターフェイスにキャストできないためです。

ではどうしたらPowerShellからVisualStudioを操作できるのでしょうか。

パッケージマネージャーコンソールでの操作

実はVisualStudioについているパッケージマネージャーコンソールからは簡単にVisualStudioの自動操作が行えます。
これはNugetがPowershellを使用してプロジェクト情報の更新を行っているためと考えられます。

パッケージマネージャーコンソールでは変数$dteにVisualStudioを操作するためのVisualStudio.DTE.16.0がすでに読み込まれており、また、Get-Interfaceコマンドレットを使用してインターフェイスを明示することが可能になっています。

Get-Interfaceの使用例

PM>  Get-Interface $dte.ActiveDocument.ProjectItem.FileCodeModel ([ENVDTE80.FileCodeModel2])

DTE          : EnvDTE.DTEClass
Parent       : System.__ComObject
Language     : {B5E9BD34-6D3E-4B5D-925E-8A43B79820B4}
CodeElements : {System.__ComObject, System.__ComObject, System.__ComObject, System.__ComObject...}
ParseStatus  : vsCMParseStatusComplete
IsBatchOpen  : False

Can't access FileCodeModel from Powershell console in Visual Studio 2017 DTE
https://stackoverflow.com/questions/45237281/cant-access-filecodemodel-from-powershell-console-in-visual-studio-2017-dte

このGet-Interfaceはパッケージマネージャーコンソール内だけで定義されているコマンドであり、その内容は以下の通りです。

PM> (Get-Command Get-Interface).Definition 

    Param(
        $Object,
        [type]$InterfaceType
    )

    [NuGetConsole.Host.PowerShell.Implementation.PSTypeWrapper]::GetInterface($Object, $InterfaceType)

NuGetConsoleはNuGetConsole.Host.PowerShell.dll内で定義されているため、これを利用することにより通常のPowerShellからもDTEのインターフェイスを明示することが可能になります。

PowerShellからVisualStudioを操作する例

では、通常のPowerShellからVisualStudioを操作する方法を考えてみます。

下記の例ではNuGetConsole.Host.PowerShell.dllを読み込んだあとDTEを利用してVisualStudioのプロジェクトから関数を取得しています。また、必要におうじて、GetInterfaceを用いてインターフェイスを明示しています。

function EnumCodeElements($elems, $level) {
    $space = " " * $level
    foreach($elem in $elems) {
        If ($elem.Kind -eq 2) { # vsCMElement.vsCMElementFunction
            # Access:
            # https://docs.microsoft.com/en-us/dotnet/api/envdte.vscmaccess?view=visualstudiosdk-2017
            Write-Host " " $space  $elem.Access $elem.Type.AsFullName   $elem.FullName
            foreach($param in $elem.Parameters) {
                Write-Host "    " $space  $param.Type.AsFullName $param.Name
            }
        }
        If ($elem.Children) {
            EnumCodeElements $elem.Children ($level+1)
        }
    }
}

function EnumProjectItem($items, $level) {
    foreach($item in $items) {
        $item = [NuGetConsole.Host.PowerShell.Implementation.PSTypeWrapper]::GetInterface($item, [ENVDTE.ProjectItem]) 
        $space = " " * $level
        Write-Host $space $item.Name
        if ($item.FileCodeModel) {
            $fileCodeModel = [NuGetConsole.Host.PowerShell.Implementation.PSTypeWrapper]::GetInterface($item.FileCodeModel, [ENVDTE.FileCodeModel])
            $fileCodeModel.Language
            EnumCodeElements $fileCodeModel.CodeElements $level
        }
        If ($item.ProjectItems) {
            EnumProjectItem $item.ProjectItems  ($level+1)
        }
    }
}

[Reflection.Assembly]::LoadFile("C:\Program Files (x86)\Microsoft Visual Studio\2019\Preview\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGetConsole.Host.PowerShell.dll") > $null

If (-not $dte) {
    $dte = [System.Runtime.InteropServices.Marshal]::GetActiveObject("VisualStudio.DTE.16.0")
}
foreach($project in $dte.Solution.Projects) {
  Write-Host "Project:" $project.Name
  EnumProjectItem $project.ProjectItems  0
}

まとめ

今回はVisualStudioをPowerShellで操作する方法を検証しました。
これを利用することでクラスの情報を取得したり、クラスを自動作成したりすることができると考えられます。
ただし、VisualStudioのDTEでFileCodeModelとして認識できるファイルはC#,VB.NET,C++とかぎられており、JavaScriptなどは認識できません。

CodeModelLanguageConstants Class
https://docs.microsoft.com/en-us/dotnet/api/envdte.codemodellanguageconstants?view=visualstudiosdk-2017

OGPってなんだってばよ

OGPってなんだってばよ

OGPがなんだかわからなかったので調べてみました。OGPとははOpen Graph protocolの略で、以下に仕様が公開されています。
https://ogp.me/

OGPを使用することで、Webページをソーシャルグラフのオブジェクトにすることができます。

たとえば、Twitterなどで、URLをつけてツイートすると以下のようにカードが表示されるURLがあります。

これは、そのURLのWebページにOGPのメタデータが設定されており、Twitterはそのメタデータを読み取りカードを作成しています。

また実際にTwitterを投稿せずとも下記のサイトにURLを張り付けることでどのようなカードが作成されるかを確認することが可能です。
https://cards-dev.twitter.com/validator

OGPのメタデータの例

たとえば、先の朝日新聞のURLは以下のようなOGPのメタデータを利用しています。

<html lang="ja">
<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# article: http://ogp.me/ns/article#">

略

<meta property="og:locale" content="ja_JP">
<meta property="og:title" content="山形)猫駅長しょこら、JRがお墨付き 制帽プレゼント:朝日新聞デジタル">
<meta property="og:url" content="https://www.asahi.com/articles/ASMDM3Q6FMDMUZHB003.html">
<!--引用可能写真=4-->
<meta property="og:image" content="https://www.asahicom.jp/articles/images/c_AS20191219003081_comm.jpg">
<meta property="og:image:height" content="314">
<meta property="og:image:width" content="600">
<meta property="og:site_name" content="朝日新聞デジタル">
<meta property="og:type" content="article">
<meta property="og:description" content=" 山形県川西町のJR米坂線・羽前小松駅のネコ駅長「しょこら」に19日、JR東日本が観光活性化の期待を込めて、市販されている駅長の制帽のミニチュアを贈った。駅を運営しているNPO法人は今後、イベントなど…">
<meta property="article:author" content="https://www.facebook.com/asahicom/">
<meta property="article:publisher" content="https://www.facebook.com/asahicom/"

この中で必須のメタデータ以下の通りです。

メタデータ 説明
og:title グラフ内に表示すべきオブジェクトのタイトル
og:type オブジェクトのタイプ。設定できる値はObject Typesのに記載されている。※朝日新聞の場合はarticleを指定している
og:url グラフの永続的なIDとして用いられるオブジェクトの基準URL
og:image グラフ内にオブジェクトを表示するときの画像のURL。og:image:width,og:image:heighなどのオプションを付けることも可能

自分でHTMLを作成する場合は、上記の4つを設定します。
og:typeはarticleかwebsiteがよく使われていそうな感じでした。

WordPressで使用する方法

以下のようなプラグインを導入すれば埋め込んでくれます。

Open Graph Protocol Framework

https://wordpress.org/plugins/open-graph-protocol-framework/

シンプルなプラグインです。
プラグインをインストールして有効化するだけで、基本的なogpのメタデータがWordPressの記事に埋め込まれます。
og:image に使用される画像はアイキャッチ画像になります。

All in one SEO Pack

https://wordpress.org/plugins/all-in-one-seo-pack/

WordPressのSEO対策をするためのツールが様々はいっているプラグインです。
機能管理から「ソーシャルメディア」を有効にすることでOGPの設定が行えるようになります。
og:image に使用する画像を細かくカスタマイズすることができます。

トスツイを検証してみる

概要

トスツイとは「@tos」をつけてツイートした場合に、フォロワーのタイムラインに表示されず、ツイートできるというテクニックです。
※「ツイートと返信」には表示されますし、検索には引っかかります。

@tosで何故タイムラインに表示されないかの考察

TwiiterのURLの構成は以下のようになっています。

https://twitter.com/ユーザー名

@ユーザー名を付けてリプライした場合、上記のURLにリンクが張られます。
このため、@tosを付けてリプライした場合、以下のURLにリンクが張られます。

https://twitter.com/tos

このページにアクセスすると「Twitterサービス利用規約」にリダイレクトされます。
つまり、tosというのは「Terms of service」の略で、サービス規約用のページで使われている文字列のため、ユーザーとして使用できないものと考えられます。
そのユーザーとして利用できない文字を利用してツイートすることで、一般のユーザのタイムラインには表示されないと考えられます。

@tosの代わりは存在するか

tosのほかにもhttps://twitter.com/homeなどはユーザー用のページでなくシステムのページに遷移します。そのため、tosと同様にシステムで使用されている文字列がトスツイとして使用できるか検証してみます。

  • URL
  • ユーザー名: リプライするユーザー
  • アカウントの存在:アカウント検索によるアカウントの有無
  • ツイート結果:ツイートした結果がどうなるか
URL ユーザー名 アカウントの存在 結果
https://twitter.com/tos @tos 存在しない 「ツイートタブ」には表示されないが、「ツイート」と返信タブでは表示される
https://twitter.com/home @home 存在する 「ツイートタブ」には表示されないが、「ツイート」と返信タブでは表示される
https://twitter.com/help @help 存在しない ユーザ名として認識されないため、リンクが作成されない。
https://twitter.com/about @about about 存在しない ユーザ名として認識されないため、リンクが作成されない。
https://twitter.com/help @help 存在しない ユーザ名として認識されないため、リンクが作成されない。
https://twitter.com/login @login 存在しない ユーザ名として認識されないため、リンクが作成されない。
https://twitter.com/logout @logout 存在する 「ツイートタブ」には表示されないが、「ツイート」と返信タブでは表示される

@homeと@logoutでも一見、@tosと同様の挙動になるように見えますが、@homeと@logoutはアカウントが存在しています。
そのため、現時点はトスツイは@tosで行っていた方が無難と考えられます。

Moodleのプラグインを作ってみる

目的

MoodleというeLearningシステムがあります。
PHPとMySQLが動けば、わりと簡単に導入することが可能です。

今回はこのMoodleのプラグインを作成してみます。

syntaxhighlighterを参考に作成しています。

ソースコード
https://github.com/mima3/aa_image/tree/master/moodle_plugin/filter_aa_image

機能

エディタで入力した「aa_image]~[/aa_image]の内容を画像に変換するプラグインをsyntaxhighlighterを参考に作成します。


以下のような入力を行ったとする。

[aa_image]
       ____ 
     /      \ 
   /  _ノ  ヽ、_  \ 
  / o゚((●)) ((●))゚o \  ほんとはVIPでやりたいんだお… 
  |     (__人__)    | 
  \     ` ⌒´     / 

[/aa_image]

[aa_image]~[/aa_image]に記載された内容を圧縮してコードに変換する。

e9zQoPAYgiHo%2FZ79EBSvwIUm0Q9hwNS%2F37MHrARZWiH%2BcXMfWE%2Fz3vd7l8SDhGDqgIx%2Bhfz3%2B%2BZrvN%2FT8Wh6%2F%2Fs9nZoKSGygTD5YMUh74%2B7HTZMfN6543Lg%2BzDPgcePyx00tj5u6HjfOf9zYApZa8Lix61HDMojRNXB3QCzqiI9%2FsmtXfDzQXGSZGphD9iB55f3eBoVHPZMObVGAiwAdysUFAA%3D%3D

このURLをパラメータとしてIMGタグを作成する。

<img src="https://needtec.sakura.ne.jp/aa_image/image?d=e9zQoPAYgiHo%2FZ79EBSvwIUm0Q9hwNS%2F37MHrARZWiH%2BcXMfWE%2Fz3vd7l8SDhGDqgIx%2Bhfz3%2B%2BZrvN%2FT8Wh6%2F%2Fs9nZoKSGygTD5YMUh74%2B7HTZMfN6543Lg%2BzDPgcePyx00tj5u6HjfOf9zYApZa8Lix61HDMojRNXB3QCzqiI9%2FsmtXfDzQXGSZGphD9iB55f3eBoVHPZMObVGAiwAdysUFAA%3D%3D"/>

コードの説明

ファイル名 説明
filter.php 出力前にコンテンツを自動的に変換する処理を記述。詳細はFiltersを参照
settings.php 設定画面の実装。詳細はsettings.phpを参照
thirdpartylibs.xml 使用しているサードパーティのライブラリの情報を記載するXMLファイル。詳細はthirdpartylibs.xmlを参照
version.php バージョン情報。詳細はversion.phpを参照
classes/privacy/provider.php 個人データを扱うか扱わないという情報をMoodleに通知するためのインターフェイス。詳細はPrivacyAPIを参照
lang/en/filter_aa_image.php 多言語対応用のファイル。画面に表示する文字列を各言語毎に用意する

settings.php

https://github.com/mima3/aa_image/blob/master/moodle_plugin/filter_aa_image/settings.php

設定画面は以下のような実装を行います。

if ($ADMIN->fulltree) {
    $setting = new admin_setting_configtext(
        'filter_aa_image/service_url',
        new lang_string('serviceurl', 'filter_aa_image'), 
        new lang_string('serviceurl_desc', 'filter_aa_image'), 
        'http://needtec.sakura.ne.jp/aa_image/image');
    $settings->add($setting);
}

この実装を行った設定ページは以下のように表示されます。

admin_setting_configtextは設定画面にテキスト入力できるコントロールを追加します。
第一引数の「filter_aa_image/service_url」は設定の名前を指定します。以下のような書式になります。

プラグイン名/一意の名称

第二引数はテキストの横に表示されるラベルの表示文字になります。
ここでは直接文字を入力することなく、lang_stringを使用して多言語対応をしています。

第三引数は説明の表示文字になります。
ここでは直接文字を入力することなく、lang_stringを使用して多言語対応をしています。

第四引数はデフォルトの値になります。

その他詳細は下記のページを参照してください。

Creating a theme settings page
https://docs.moodle.org/dev/Creating_a_theme_settings_page

filter.php

出力前にコンテンツを自動的に変換する処理を記述します。

class filter_aa_image extends moodle_text_filter {

    public function filter($text, array $options = array()) {
        if (!is_string($text) || empty($text)) {
            return $text;
        }

        $re = "~\[aa_image\](.*?)\[/aa_image\]~isu";
        $result = preg_match_all($re, $text, $matches);
        if ($result > 0) {
            foreach ($matches[1] as $idx => $code) {
                $code = str_replace(
                    ['>','<','<pre>', '</pre>', '<p>', '</p>', '<br>', ' '],
                    ['<', '>','', '', '', "", "\n", " "],
                    $code);
                $key = base64_encode(gzdeflate($code, 9));
                $newcode = '<p><img src="' . get_config('filter_aa_image', 'service_url') . '?d=' . urlencode($key). '"/></p>';
                                $text = str_replace($matches[0][$idx], $newcode, $text);
            }
        }

        return $text;
    }

moodle_text_filterを継承したクラスでfilterメソッドを実装することで実現できます。
やっている内容は[aa_image]で始まって[/aa_image]で終わる箇所を抜き出して置き換えをしています。
設定画面で設定した内容はget_configを使用して取得できます。

プラグインのインストール方法

下記のフォルダをZIP形式で圧縮します。
https://github.com/mima3/aa_image/tree/master/moodle_plugin/filter_aa_image

この圧縮したZIPをmoodleにアップロードします。

管理者でログインした後にサイドメニューの「サイト管理」を選択後、「プラグイン」タブを選びます。そこから「プラグインをインストールする」を選びます。

ZIPパッケージにZIPファイルをドラッグアンドドロップしたのちに、「ZIPファイルからプラグインをインストールする」を選択します。

警告の内容を確認し、問題なければインストールを続けます。

moodleデータベースを更新します。

更新結果が表示されるので成功の場合は「続ける」を選択します。

プラグインの管理画面が表示されるので必要に応じて設定を変更して「変更を保存する」を選択します。

この時点でプラグインのインストールは完了しましたが、プラグインの機能は有効化されていないません。

プラグインの有効化の手順

管理者でログインした後にサイドメニューの「サイト管理」を選択後、「プラグイン」タブを選びます。そこから「プラグインの概要」を選びます。

プラグイン名で検索して、そのカテゴリーの「設定」ボタンを押します。今回の場合はテキストフィルタの設定になります。

プラグイン名で検索して「有効?」の列をONにします。

プラグインのアンインストール方法

管理者でログインした後にサイドメニューの「サイト管理」を選択後、「プラグイン」タブを選びます。そこから「プラグインの概要」を選びます。

プラグイン名で検索してアンインストールを実行します。

ブラウザに表示する画像を動的に作成する

目的

ブラウザに表示する画像を動的に作成するというテクニックは古来より使われてきました。
たとえば、アクセスカウンターや数式の描画などに使用されています。
今回はその使用例を見るとともに、実際に自分で作成してみることにします。

実例

アクセスカウンターの例

以下のページは無料アクセスカウンターを提供しています。
http://www.rays-counter.com/

このページでアクセスカウンターを作成すると以下のようなタグを含んでいます。

http://www.rays-counter.com/d460_f6_022/5ec064f0698a5/

アクセスカウンタ

該当のURLにアクセスがあるたびに、カウンターを増やして、その内容を画像に変換して応答しています。

mimetex

http://www.forkosh.com/mimetexmanual.html
mimetexはC言語で実装されたCGIで、使用するとimgタグを使用して数式を描画することができます。

たとえば以下のようなタグを記載します。

<img src="https://needtec.sakura.ne.jp/mimetex/mimetex.cgi?\frac{-b\pm\sqrt{b^2-4ac}}{2a}">

この内容をブラウザで確認すると以下のような画像になります。

以下のページでmimetexにあたえるパラメータを試せるようにしています。
http://needtec.sakura.ne.jp/mimetex/index.php

PlantUMLのWordPressのプラグイン

WordPressのプラグインにPlantUML Renderというものがあります。
https://ja.wordpress.org/plugins/plantuml-renderer/

以前、導入方法を紹介しましたが、PlantUML Renderの仕組みも、imgタグを使用して与えたパラメータにより画像を動的に作成することで実現しています。

また、当該プラグインはPlantUMLの公式のWebサイトのURLをりようしているので、実際にWordPressをインストールしないでも、この仕組みを確認することができます。
https://plantuml.com/en/demo-javascript-synchronous

このページは以下のようなテキストエリアと画像が存在します。

テキストエリアの内容

Bob->Alice : hello

画像
UML

この画像のURLは以下になります。

http://www.plantuml.com/plantuml/img/SyfFKj2rKt3CoKnELR1Io4ZDoSa70000

「SyfFKj2rKt3CoKnELR1Io4ZDoSa70000」という値はテキストエリアの内容によって変化します。

たとえばテキストエリアの内容を以下のように変更して送信ボタンを押してみましょう。

ボブ->アリス : こんにちは

この場合の画像とそのURLは以下のようになります。

http://www.plantuml.com/plantuml/img/0IG0s__ZWvpZWvOjFkE2ekE3gkE2kI0w8EE1a-E2a-E1g-E1eUE1hme0

「SyfFKj2rKt3CoKnELR1Io4ZDoSa70000」という値が「0IG0s__ZWvpZWvOjFkE2ekE3gkE2kI0w8EE1a-E2a-E1g-E1eUE1hme0」に変更されたことが確認できます。
この値はテキストエリアの内容を圧縮したものになっており、サーバー側はこの内容を解凍してから、その内容に応じた画像を生成しています。

AAの画像生成プログラムを作ってみる

ではこれらの先駆者の方法を利用してAAの画像生成プログラムを作ってみます。

デモサイト
https://needtec.sakura.ne.jp/aa_image/main

コード
https://github.com/mima3/aa_image

GETの受付と応答

imgタグで指定したsrcのURLにGETでリクエストされるので、その応答を作成する必要があります。
今回はPHPの軽量フレームワークである、Slim Frameworkを使用しました。

実際に画像を生成するリクエストの箇所は以下になります。
https://github.com/mima3/aa_image/blob/master/www/index.php#L89

$app->get('/image', function (Request $request, Response $response, $args) {
    // この時点でエンコードをしてくれる
    $key = $request->getQueryParams()['d'];

    $imageModel = $this->get('imageModel');
    $image = null;
    if (strlen($key) > 1024 * 10) {
        $response->getBody()->write("文字数が多すぎるため作成できません。");
        return $response->withStatus(500);
    }

    $rec = $imageModel->get($key);
    if ($rec) {
        $image = $rec->data;
    } else {
        $image = convertImage($key, $this->get('config')['FONT_PATH'], 12);
        if (strlen($image) > 1024 * 10) {
            $response->getBody()->write("画像サイズが大きすぎるため作成できません。");
            return $response->withStatus(500);
        }

        $imageModel->append($key, $image);
    }

    // 
    $response->getBody()->write($image);
    return $response
        ->withHeader('Content-Type', 'image/png')
        ->withStatus(200);
});

やっていることとしてはconvertImageという関数でクエリーパラメータからPNG画像を作成します。
その後、その内容を$responseのwriteを使用して応答を書き込み、ヘッダーを指定してます。

使いどころ

画像としてAAを作成できるため、環境によってのズレが生じません。
たとえば、QiitaやMoodleでAAを張るとズレますが、この仕組みを利用するとズレが生じなくなります。

以下は実際に作成したmoodleのプラグインになります。
https://github.com/mima3/aa_image/tree/master/moodle_plugin/filter_aa_image

懸案事項

この手法の問題としてURLが長くなってしまうことにあります。
Hypertext Transfer Protocol — HTTP/1.1 (RFC 2616)には長さの規定はないものの、サーバーならびにブラウザに上限は存在するので、何千、何万ものパラメータを渡すことはできないでしょう。

参考:

ムッシューのIT備忘録- URLの長さの上限について
https://mussyu1204.myhome.cx/wordpress/it/?p=693

さがしていたあの曲まとめ

さがしていたあの曲

曲のフレーズはわかるが、曲名がわからなかったあの曲を調べてみました。

パンチャンドラムのテーマ

https://en.wikipedia.org/wiki/Scotland_the_Brave

スコットランドの愛国的な歌で、けっしてパンチャンドラムをたたえる歌ではない。
Wikipediaによると、19世紀後半に演奏されはじめて現在一般的に使用されている歌詞は、1950年頃に作詞されたものらしい。

ソヴィエトマーチ

偉大なるソ連をたたえる作詞作曲がアメリカ人の歌。
実はRedAlertという資本主義の犬が作ったビデオゲームに出てくる曲で別にソ連の国家ではない。

マッスロイドのテーマ

ムキ姉さんが登場する時のテーマソング。

Bon Jovi氏 の 「It's My Life」という曲。

彼がボディービルダーだったり筋肉を誇っているという事実は確認できない。

勝利確定のBGM

【ゆっくり解説】世界の奇人・変人・偉人紹介を中心によくつかわれる勝利確定時にながれるBGM.

フリーBGM素材「甘茶の音楽工房」で公開されている「残業戦士」という曲
https://amachamusic.chagasi.com/image_shissoukan.html

使われるシーンのわりには曲のタイトルが悲劇的。

鼻から牛乳

嘉門達夫達夫氏の替え歌で出てくる曲
https://www.amazon.co.jp/dp/B000064XGL

元はトッカータとフーガ ニ短調というバッハの曲。風評被害枠その1。

なお、バッハが鼻から牛乳を出したか否かは検証できない。

The Carol of the Old Ones - 旧支配者のキャロル -

元はCarol of the Bellsというウクライナの民謡を元につくられたクリスマスキャロルで別に、邪神を復活させるための歌ではない。風評被害枠その2.

住所から座標をもとめるジオコーディングの方法

目的

地名、住所が示す場所に対して、地理座標を与えるジオコーディング(geocoding)を行う方法を説明します。

Google Map

以前はGoogleMapのジオコーディングのAPIがお手軽に使えましたが、昨今は、APIキー取得にはクレジットカード登録が必須になってしまい使用するための敷居が高くなっています。
https://developers.google.com/maps/documentation/geocoding/start?hl=ja

Yahoo!ジオコーダAPI 

1日5万回の実行まで使えます。
https://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/geocoder.html

実際に当方が使用したPythonでの実装例は以下のようになります。

https://github.com/mima3/yakusyopdf/blob/master/create_geo.py

特に注意するのはrecursiveパラメータで、もし、ぴったしの住所がなかった場合、上のレベルで検索します。
recursiveパラメータがtrueの場合、「東京都千代田区ほげ町」が存在しなければ「東京都千代田区」で検索した結果をかえしてくれます。recursiveパラメータがfalseの場合は上位レベルでの検索は行いません。

なお、座標データはXMLまたはJSONで取得可能です。

ジオコーダDAMS(Distributed Address Matching System)

いままではWebサービスでしたがDAMSはローカルにジオコーディング用の環境を作成できます。
http://newspat.csis.u-tokyo.ac.jp/geocode/modules/dams/index.php?content_id=1
APIの制限を気にせずにローカルの環境で実行できるのは大きな強みだと考えられます。

その他、Googleのジオコーダとの違いは以下に記載されています。
http://newspat.csis.u-tokyo.ac.jp/geocode/modules/smartfaq/category.php?categoryid=4

pydams

PythonからDAMSを使用できるライブラリです。
https://github.com/hottolink/pydams

ただしこのpythonのライブラリに指定しているパッチを当てると一部の住所の検索ができなくなりますので注意が必要です。(修正済み)
https://github.com/hottolink/pydams/issues/5