RX63Nマイコン sprintf関数の振る舞いがおかしい

皆様こんにちは。

sprintf関数の振る舞いで、プログラムを動作しているとバグ?が発生し

質問させて頂きました。現在の環境は以下の通りとなります。

開発環境:HEW4

コンパイラ:RXファミリ用C/C++コンパイラ

使用している言語:C/C++

使用しているマイコン:AP-RX63N-0A

 

用途としましては、char型配列に(1024byte)に半角文字列(数値もありますので書式付フォーマット使用)を挿入して

データを送信する仕様であります。

動作していくとある回数(不定)でchar型配列に["ABC:%4d volt"]のように配列が入り、数値が入らず書式付フォーマットの%dが

そのまま配列に入るようになりました。マイコンをリセットしないとバグは解決しませんでした。

バッファは十分余裕があり、オーバーランはしていないようです。

原因が分からなかったため、質問させて頂きました。

以上宜しくお願い致します。

  • 再現性がある不具合なら動作がおかしくなった以降で sprintf() の動作をデバッガで追いかければ良いのでは?
  • 原因究明の前にアレコレ弄ってるうちに「いつの間にか直った(現象が現れなくなった)」というのが一番厄介なので、とりあえず問題の発生の確認できているプログラムをプロジェクト丸ごとアーカイブするか何かで保存されることをお勧めします。

  • わわいです
    まず疑われるのは、割り込み関数などでそのsprintf関数を実行していないか、あるいは、他の標準関数を割り込みで使用していないか調べてみることでしょう。
    sprintf関数などの大規模関数ではリエントラントとしては作られていないので、割り込み関数で実行すると不具合を起こすことがあります。
    また、スタックが足りているかも調べてみるのもいいかと思います。

    sprintf関数 などは、使い勝手がいい反面、汎用的に使用できるように、かなりな大規模なコード量となり、メモリ消費量も多くなっています
    できれば、sprintfを使わずに、自分のコードでその文字列を生成するようにした方がいいでしょうね。

    私の持論ですが、こういう標準関数ってのは、あくまでコンパイラが用意してくれるお便利機能であって、そーそーやたらめったら使うもんではない、とおもっています
  • fujita nozomuさん
    回答ありがとうございます。
    デバッガーを使用して、当該関数の前後をステップ実行してみると
    sprintf関数実行後の命令後char型配列に%dが出力されるバグが
    発生致しました。
    プログラムは少しでも変更するたびに、プロジェクト本体毎に
    バックアップを取り続けております。

    わわいさん
    いつも丁寧なご回答ありがとうございます。
    sprintf関数の実行場所になりますが、タイマ割込み中に実行しています

    void Excep_CMT1_CMI1(void)
    {
    sprintf(disp_buff, "C1:%1.02fV C2:%1.02fV C3:%1.02fV", m_AdVolt[CH1], AdVolt[CH2], AdVolt[CH3]);
    send(disp_buff);
    }
    上記のように関数を実行しており、最初の1~2回目はバグは無いんですが
    3回目以降などに%dが直接出力されました。
    以降はリセットしないと解決しません。

    事象発生時send関数(RS232C通信送信)実行時にブレイクポイントを置くと
    disp_buffのchar型配列に確かに%dが入っていました。


    スタックの調べ方はちょっと分からないですが教えてもらうことは可能でしょうか

    sprintf関数は便利ですが、そういう問題点もあるんですね・・・
    どうにかして自分のコードでその文字列を生成するようにした
    ソースを組み込んで見ます。
  • > char型配列に["ABC:%4d volt"]のように配列が入り、数値が入らず書式付フォーマットの%dがそのまま配列に入るようになりました。



    > sprintf(disp_buff, "C1:%1.02fV C2:%1.02fV C3:%1.02fV", m_AdVolt[CH1], AdVolt[CH2], AdVolt[CH3]);

    > 3回目以降などに%dが直接出力されました。

    で書式が異なっていますが、相談されてる問題の現象は正確なものですか?

  • わわいです。
    >スタックの調べ方はちょっと分からないですが教えてもらうことは可能でしょうか
    デバッガで動作させ、その動作がおかしくなったときに、ブレークさせて、スタックポインタがどういう値を指しているかみてみる、とか、
    スタックの領域を全域0xaa とか0x55 とかの値で埋めておき(というよりRXあたりであればRAMを全域埋めておくってのがいいのかな)、プログラムを実行させたあとで、どこまで領域を使用しているかを調べてみることでしょうね。

    話を聞く限り、スタックが領域を超えてしてしまっているってかんじですねー。
  • 最近の CC-RX の更新情報を見てみると

    tool-support.renesas.com/.../CC-RX_List.html
    > V2.02.00 → V2.03.00 への変更点 (2015/2/5)

    > 2. 標準ライブラリのリエントラント化対応 [C標準ライブラリ(ライブラリジェネレータ)]
    > ライブラリジェネレータ(lbgrx)に-reentオプションの指定により、リエントラント化されたC標準ライブラリを
    > 作成できるようにしました。生成されたC標準ライブラリに、3つの低水準関数をお客様にて 作成いただくことで、
    > rand/srand関数を除くC言語標準ライブラリ関数をリエントラント化することができます。

    とありますが、HEW で使用しているであろうもっと古い版の CC-RX でのライブラリはリエントラントにはなってないと思われます。割り込み中で sprintf() を使用されるのであればその点注意が必要です。

  • printfやsprintfはかなり大きなヒープを使います。ヒープがエリアを設定していますか?
  • 訂正:

    > HEW で使用しているであろうもっと古い版の CC-RX でのライブラリはリエントラントにはなってないと思われます。

    「ビルド(B)」 → RX Standard Toolchain → 「標準ライブラリ」タブ → カテゴリ(Y): 「オブジェクト」 → 「リエントラントライブラリを生成(G)」というチェックがありリエントラントなライブラリを生成することも対応しているようです。
  • 「リエントラントライブラリを生成(G)」にチェックを入れてビルドすると

    L2310 (E) Undefined external symbol "_errno_addr" referenced in "xwctomb"

    というエラーが出るな。

    void* errno_addr;

    とか宣言すりゃ消えるけど正しくはどうすんだろう?