程式碼中的追蹤程序

您可以指示編譯器插入追蹤 您已定義的程序 的呼叫,以協助除錯或計時執行其他程序。

若要追蹤程式中的程序,您必須指定要追蹤的程序。 您也必須提供自己的追蹤程序。 如果您在不提供追蹤程序的情況下啟用追蹤,則會收到鏈結器錯誤,有關稱為 __func_trace_enter__func_trace_exit及可能 __func_trace_catch的未定義符號。

指定要追蹤的程序

-qfunctrace 編譯器選項可控制程式中所有 非行內使用者定義程序 及所有概述編譯器產生程序的追蹤。 如果您有興趣追蹤特定的外部或模組程序,您可以使用 -qfunctrace+-qfunctrace- 編譯器選項。 您也可以指定 NOFUNCTRACE 指引,以停用追蹤整個模組、外部程序、模組程序或內部程序。

可以追蹤的內容

追蹤適用於程式、外部程序、 非本質 模組程序及內部程序。

除非針對概述的使用者程式碼 (例如 OpenMP 程式) 產生編譯器產生的程序,否則不會追蹤這些程序。 在這些情況下,概述程序的名稱會包含原始使用者程序的名稱作為字首。

無法追蹤行內程序及陳述式函數,因為它們不存在於執行檔中。

為了避免無限遞迴,無法追蹤使用者定義的追蹤程序。 同樣地,對於從使用者定義的追蹤程序呼叫的程序,必須停用追蹤。

如何撰寫追蹤程序

您可以在 Fortran、C 或 C++ 中實作追蹤程序。

若要在 Fortran中實作追蹤程序,程序的性質必須與下列介面中指定的性質相同:
SUBROUTINE routine_name(procedure_name, file_name, line_number, id)
  USE, INTRINSIC :: iso_c_binding
  CHARACTER(*), INTENT(IN) :: procedure_name
  CHARACTER(*), INTENT(IN) :: file_name
  INTEGER(C_INT), INTENT(IN) :: line_number
  TYPE(C_PTR), INTENT(INOUT) :: id
END SUBROUTINE
其中 routine_name 是外部或模組程序的名稱。
然後,您必須以下列其中一種方式,告知編譯器使用您的子常式作為追蹤程序:
  • 使用 -qfunctrace_xlf_enter-qfunctrace_xlf_exit-qfunctrace_xlf_catch 編譯器選項。
  • 使用 FUNCTRACE_XLF_ENTERFUNCTRACE_XLF_EXITFUNCTRACE_XLF_CATCH 指引。

當您指定這些選項或指引時, XL Fortran 會產生稱為 __func_trace_enter__func_trace_exit__func_trace_catch 的封套程序,以呼叫對應的追蹤程序。 這些 wrapper 可透過將虛擬引數從 C 原型轉換至先前說明的介面,來與 C 及 C++ 進行交互作業。 因此, routine_name 不得命名為 __func_trace_enter__func_trace_exit__func_trace_catch。 此外,您的程式不得包含多個追蹤程序。

以 C 或 C++ 撰寫追蹤程序需要您直接提供 __func_trace_enter__func_trace_exit__func_trace_catch 程序。 它們必須具有下列原型:
  • void __func_trace_enter(const char *const procedure_name, const char *const file_name, int line_number, void **const id);
  • void __func_trace_exit(const char *const procedure_name, const char *const file_name, int line_number, void **const id);
  • void __func_trace_catch(const char *const procedure_name, const char *const file_name, int line_number, void **const id);
附註: 如果您以 C++ 撰寫追蹤程序,則必須宣告它們 extern "C"

XL Fortran 會在程序進入和結束時插入對追蹤程序的呼叫。 它會傳遞所追蹤的程序名稱、包含所追蹤之進入或跳出點的檔案名稱,以及行號。 它也會傳遞在程式開頭起始設定為 C_NULL_PTR 的靜態指標位址。 此指標可讓您在進入追蹤程序中儲存任意資料,並在結束及捕捉程序中存取此資料。 如需詳細資料,請參閱「範例」一節。 因為此指標位於靜態記憶體中,所以在追蹤執行緒或遞迴程序時可能需要額外步驟。

範例追蹤程序

XL Fortran 在目錄中提供範例追蹤程序。 您可以使用這些程序來進行簡式追蹤,也可以修改它們來進行更複雜的追蹤。
  • tracing_routines.c: 提供以 C 撰寫的追蹤程序。 當您不需要存取 Fortran 模組,且有可能遞迴輸入/輸出時,此檔案非常有用。
  • tracing_routines.f90: 提供以 Fortran撰寫的追蹤程序。 當您需要存取追蹤程序中的 Fortran 模組或本質時,這個檔案很有用。
下列範例說明如何使用範例來進行簡式追蹤:
> cat helloworld.f
print *, 'hello world'
end
> cc -c /opt/IBM/xlf/15.1.0/samples/functrace/tracing_routines.c
> xlf95 helloworld.f -qfunctrace tracing_routines.o
** _main   === End of Compilation 1 ===
1501-510  Compilation successful for file helloworld.f.
> ./a.out
{ _main (helloworld.f:1)
 hello world
} _main (helloworld.f:2)

>

追蹤限制

程序追蹤功能具有下列限制:
  • 程序無法與其 ENTRY 點分開追蹤。 全部都被追蹤或沒有任何追蹤。 即使在追蹤 ENTRY 點時,也會將程序名稱傳遞給追蹤程序。 行號可協助識別在此情況下正在追蹤的項目。
  • Fortran 標準需要純程序無負面影響。 編譯器在最佳化程式時使用此假設。 如果您啟用純程序的追蹤,則追蹤程序不得以產生負面影響的方式變更程式狀態。
  • Fortran 標準強制限制遞迴輸入/輸出。 如果您在 Fortran中撰寫追蹤程序,則必須小心不要違反這些規則。
    下列範例具有列印陳述式,其中 I/O 項目是函數呼叫 (foo) 的結果。 在此情況下,追蹤程序在外部檔案上具有 I/O 是不合法的:
    > cat recursive.f
    integer function test()
      test = 1
    end function
    
    integer test
    print *, test() ! test must not have I/O on external unit
    end
    > xlf95 -c /opt/IBM/xlf/15.1.0/samples/functrace/tracing_routines.f90
    ** my__func_trace_enter   === End of Compilation 1 ===
    ** my__func_trace_exit   === End of Compilation 2 ===
    ** my__func_trace_catch   === End of Compilation 3 ===
    1501-510  Compilation successful for file tracing_routines.f90.
    > xlf95 recursive.f tracing_routines.o -qfunctrace
    ** test   === End of Compilation 1 ===
    ** _main   === End of Compilation 2 ===
    1501-510  Compilation successful for file recursive.f.
    > ./a.out
    { _main (recursive.f:6)
    XL Fortran (I/O initialization): I/O recursion detected.
    IOT/Abort trap
    >
    附註: 您可以撰寫 C 中的追蹤程序來暫時解決此問題。 如需範例,請參閱 tracing_routines.c 範例檔 範例追蹤程序 小節中說明
  • 當最佳化您的程式時,編譯器會重新排序程式碼並移除已停用的程式碼。 因此,當啟用最佳化時,傳遞至追蹤程序的行號可能不精確。

範例

在下列範例中, -qfunctrace 用來測量每一個外部程序所花費的時間。 FUNCTRACE_XLF_ENTERFUNCTRACE_XLF_EXIT 指引用來指定程序 my_entermy_exit 作為追蹤程序。 NOFUNCTRACE 指引用來停用 main_program的追蹤:
> cat example.f
! Designate my_enter as a tracing procedure that should be called
! on procedure entry
!ibm* functrace_xlf_enter
subroutine my_enter(procedure_name, file_name, line_number, id)
  use, intrinsic :: iso_c_binding
  use, intrinsic :: xlfutility
  character(*), intent(in) :: procedure_name, file_name
  integer(c_int), intent(in) :: line_number
  type(c_ptr), intent(inout) :: id

  integer(kind=time_size), pointer :: enter_count

  ! Store the time we entered the procedure being traced into id.
  if (.not. c_associated(id)) then
    allocate(enter_count)
    enter_count = time_()
    id = c_loc(enter_count)
  end if

  print *, 'Entered procedure ', procedure_name, ' at ( ',  &
           file_name, ' :', line_number, ').'
end subroutine

! Designate my_exit as a tracing procedure that should be called
! on procedure exit
!ibm* functrace_xlf_exit
subroutine my_exit(procedure_name, file_name, line_number, id)
  use, intrinsic :: iso_c_binding
  use, intrinsic :: xlfutility
  character(*), intent(in) :: procedure_name, file_name
  integer(c_int), intent(in) :: line_number
  type(c_ptr), intent(inout) :: id

  integer(kind=time_size), pointer :: enter_count
  integer(kind=time_size) exit_count, duration

  ! id should have been associated in my_enter with the time we
  ! entered the procedure being traced.  Find the elapsed time.
  if (c_associated(id)) then
    exit_count = time_()
    call c_f_pointer(id, enter_count)
    duration = exit_count - enter_count
  else
    stop "error!"
  endif

  print *, 'Leaving procedure ', procedure_name, ' at ( ',  &
           file_name, ' :', line_number, ').'
  print *, 'Spent', duration, 'seconds in ', procedure_name, '.'
end subroutine

! sub2 will be traced
subroutine sub2
  call sleep_(3)
end subroutine

! sub1 will be traced
subroutine sub1
  call sleep_(5)
  call sub2
end subroutine

! Do not want to trace main_program
!ibm* nofunctrace
program main_program
  call sub1
end program
> xlf95 example.f -qfunctrace
** my_enter   === End of Compilation 1 ===
** my_exit   === End of Compilation 2 ===
** sub2   === End of Compilation 3 ===
** sub1   === End of Compilation 4 ===
** main_program   === End of Compilation 5 ===
1501-510  Compilation successful for file example.f.
> ./a.out
 Entered procedure sub1 at ( example.f : 59 ).
 Entered procedure sub2 at ( example.f : 54 ).
 Leaving procedure sub2 at ( example.f : 55 ).
 Spent 3 seconds in sub2.
 Leaving procedure sub1 at ( example.f : 61 ).
 Spent 8 seconds in sub1.
>

相關資訊

  • 如需 -qfunctrace 編譯器選項的詳細資料,請參閱 XL Fortran 編譯器參照中的 -qfunctrace
  • 如需 -qfunctrace_xlf_catch-qfunctrace_xlf_enter-qfunctrace_xlf_exit 編譯器選項的詳細資料,請參閱 XL Fortran 編譯器參照 XL Fortran 編譯器選項的詳細說明 一節。
  • 如需 FUNCTRACE_XLF_CATCHFUNCTRACE_XLF_ENTERFUNCTRACE_XLF_EXIT 指引的詳細資料,請參閱 XL Fortran Language Reference中的 詳細指引說明 一節。
  • 如需 NOFUNCTRACE 指引的詳細資料,請參閱 XL Fortran Language Reference中的 NOFUNCTACE