自定義 LLVM PASS 實現 函數耗時插樁統計

2021-03-02 知識小集

作者 | 0x1306a94 
來源 | https://blog.0x1306a94.com/,點擊閱讀原文查看作者更多文章

下載源碼

mkdir LLVM
cd LLVM
wget https://github.com/llvm/llvm-project/archive/llvmorg-11.0.0.zip
unzip llvmorg-11.0.0.zip

編寫編譯腳本

在 LLVM 目錄下,創建一個build.sh文件,拷貝下面內容

#!/usr/bin/env bash

set -e
set -o pipefail
set -u


ROOT_DIR=`pwd`
SOURCE_CODE_DIR=$ROOT_DIR/llvm-project-llvmorg-11.0.0
BUILD_DIR=$ROOT_DIR/build_dir
LLVM_BUILD_DIR=$BUILD_DIR/build_llvm
LLVM_OUT_DIR=$BUILD_DIR/build_llvm_out

CLANG_BUILD_DIR=$BUILD_DIR/build_clang
CLANG_OUT_DIR=$BUILD_DIR/build_clang_out

XCODE_BUILD_DIR=$BUILD_DIR/build_xcode
XCODE_OUT_DIR=$BUILD_DIR/build_xcode_out

function build_llvm() {
echo "generate llvm ninja build config ..."
cd $LLVM_BUILD_DIR
cmake -G "Ninja" \
-DCMAKE_OSX_DEPLOYMENT_TARGET="10.14" \
-DCMAKE_OSX_SYSROOT="macosx" \
-DCMAKE_OSX_ARCHITECTURES='x86_64' \
-DLLVM_TARGETS_TO_BUILD="X86" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=$LLVM_OUT_DIR \
$SOURCE_CODE_DIR/llvm
echo "ninja build llvm ..."
ninja install
}

function build_clang() {
echo "generate clang ninja build config ..."
cd $CLANG_BUILD_DIR
cmake -G "Ninja" \
-DCMAKE_PREFIX_PATH=$LLVM_OUT_DIR/lib/cmake/llvm \
-DCMAKE_OSX_DEPLOYMENT_TARGET="10.14" \
-DCMAKE_OSX_SYSROOT="macosx" \
-DCMAKE_OSX_ARCHITECTURES='x86_64' \
-DLLVM_TARGETS_TO_BUILD="X86" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=$CLANG_OUT_DIR \
$SOURCE_CODE_DIR/clang
echo "ninja build clang ..."
ninja install
}

function generate_xcode_project() {
cd $XCODE_BUILD_DIR
cmake -G "Xcode" \
-DCMAKE_OSX_DEPLOYMENT_TARGET="10.14" \
-DCMAKE_OSX_SYSROOT="macosx" \
-DCMAKE_OSX_ARCHITECTURES='x86_64' \
-DLLVM_TARGETS_TO_BUILD="X86" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=$XCODE_OUT_DIR \
$SOURCE_CODE_DIR/llvm
}


function clear_build() {
echo "clear build dir"
rm -rf $BUILD_DIR
}

function usage() {
echo "Usage:"
echo "build.sh -b -x"
echo "Description:"
echo "-b, build llvm and clang"
echo "-x, generate xcode project"
echo "-c, clear build dir"
exit 0
}

function onCtrlC () {
clear_build
exit 0
}

BUILD_FLAG=0
GENERATE_XCODE_FLAG=0
CLEAR_BUILD_FLAG=0
while getopts 'hbxc' OPT; do
case $OPT in
b) BUILD_FLAG=1;;
x) GENERATE_XCODE_FLAG=1;;
c) CLEAR_BUILD_FLAG=1;;
h) usage;;
?) usage;;
esac
done


if [[ $CLEAR_BUILD_FLAG == 1 ]]; then
clear_build
fi

if [[ $BUILD_FLAG == 1 ]]; then

# trap 'onCtrlC' INT


startTime_s=`date +%s`

if [[ $GENERATE_XCODE_FLAG == 1 ]]; then
mkdir -p $XCODE_BUILD_DIR
generate_xcode_project
else
mkdir -p $LLVM_BUILD_DIR $CLANG_BUILD_DIR
build_llvm
build_clang
fi

endTime_s=`date +%s`
sumTime=$[ $endTime_s - $startTime_s ]

echo "開始: $(date -r $startTime_s +%Y%m%d-%H:%M:%S)"
echo "結束: $(date -r $endTime_s +%Y%m%d-%H:%M:%S)"
hour=$[ $sumTime / 3600 ]
minutes=$[ ($sumTime - ($hour * 3600)) / 60 ]
seconds=$[ ($sumTime - ($hour * 3600)) % 60 ]
echo "耗時:$hour:$minutes:$seconds"
fi

創建PASS工程

進入llvm-project-llvmorg-11.0.0/llvm/lib/Transforms 目錄

將 Hello 目錄拷貝一份,並重命名為 FunctionCallTime

在 llvm-project-llvmorg-11.0.0/llvm/lib/Transforms/CMakeLists.txt 後面追加一行 add_subdirectory(FunctionCallTime)

在 llvm-project-llvmorg-11.0.0/llvm/lib/Transforms/LLVMBuild.txt subdirectories 後面追加 FunctionCallTime

修改 FunctionCallTime/CMakeLists.txt 為下面內容

# If we don't need RTTI or EH, there's no reason to export anything
# from the hello plugin.
if( NOT LLVM_REQUIRES_RTTI )
if( NOT LLVM_REQUIRES_EH )
set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/FunctionCallTime.exports)
endif()
endif()

if(WIN32 OR CYGWIN)
set(LLVM_LINK_COMPONENTS Core Support)
endif()

set(LLVM_LINK_COMPONENTS Demangle)

add_llvm_library( LLVMFunctionCallTime MODULE BUILDTREE_ONLY
FunctionCallTime.cpp

DEPENDS
intrinsics_gen
PLUGIN_TOOL
opt
)

將 Hello.cpp 重命名為 FunctionCallTime.cpp, Hello.exports 重命名為 FunctionCallTime.exports

將FunctionCallTime.cpp 內容替換為下面

//
// function_call_time.cpp
// function-call-time
//
// Created by king on 2020/12/18.
//

#include <iostream>

#include "llvm/ADT/Statistic.h"
#include "llvm/Demangle/Demangle.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Value.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"

using namespace llvm;
namespace {
struct FunctionCallTimePass : public FunctionPass {
static char ID;

static StringRef InsertFuncNamePrefix;
static StringRef BeginFuncName;
static StringRef EndFuncName;

FunctionCallTimePass()
: FunctionPass(ID) {}

bool runOnFunction(Function &F) override {

if (F.empty()) {
return false;
}
std::string annotation = readAnnotate(&F);

auto funcName = F.getName();
// 可以通過 __attribute__((__annotate__(("ignore_appletrace"))))
// 忽略當前函數
if (annotation.length() > 0 &&
StringRef(annotation).contains("ignore_appletrace")) {
errs() << "function-call-time: " << funcName << " 忽略 annotation"
<< "\n";
return false;
}
// Objective-C 方法前面有 \x01
if (funcName.front() == '\x01') {
funcName = funcName.drop_front();
}

if (funcName.startswith("__Z") || funcName.startswith("_Z")) {
// C++ 函數
std::string str = funcName.str();
std::string demangled = demangle(str);
funcName = StringRef(demangled);
}
// 將統計代碼調用過濾掉
if (funcName.startswith("appletrace")) {
return false;
}
// 如果是插樁的函數直接跳過
if (F.getName().startswith(FunctionCallTimePass::InsertFuncNamePrefix)) {
return false;
}

// 只統計 Objective-C 方法調用
if (funcName.startswith("+[") || funcName.startswith("-[")) {
// 2. 插入開始
if (!insertBeginInst(F)) {
return false;
}
// 3. 插入結束
insertEndInst(F);
return false;
}
return false;
}

private:
std::string readAnnotate(Function *f) {
std::string annotation = "";

// Get annotation variable
GlobalVariable *glob =
f->getParent()->getGlobalVariable("llvm.global.annotations");

if (glob != NULL) {
// Get the array
if (ConstantArray *ca = dyn_cast<ConstantArray>(glob->getInitializer())) {
for (unsigned i = 0; i < ca->getNumOperands(); ++i) {
// Get the struct
if (ConstantStruct *structAn =
dyn_cast<ConstantStruct>(ca->getOperand(i))) {
if (ConstantExpr *expr =
dyn_cast<ConstantExpr>(structAn->getOperand(0))) {
// If it's a bitcast we can check if the annotation is concerning
// the current function
if (expr->getOpcode() == Instruction::BitCast &&
expr->getOperand(0) == f) {
ConstantExpr *note =
cast<ConstantExpr>(structAn->getOperand(1));
// If it's a GetElementPtr, that means we found the variable
// containing the annotations
if (note->getOpcode() == Instruction::GetElementPtr) {
if (GlobalVariable *annoteStr =
dyn_cast<GlobalVariable>(note->getOperand(0))) {
if (ConstantDataSequential *data =
dyn_cast<ConstantDataSequential>(
annoteStr->getInitializer())) {
if (data->isString()) {
annotation += data->getAsString().lower() + " ";
}
}
}
}
}
}
}
}
}
}
return annotation;
}

bool insertBeginInst(Function &F) {
// 0.函數最開始的BasicBlock
LLVMContext &context = F.getParent()->getContext();
BasicBlock &BB = F.getEntryBlock();

// 1. 獲取要插入的函數
FunctionCallee beginFun = F.getParent()->getOrInsertFunction(
FunctionCallTimePass::BeginFuncName,
FunctionType::get(Type::getVoidTy(context),
{Type::getInt8PtrTy(context)}, false));

errs() << "function-call-time: " << BB.getParent()->getName() << " begin\n";
// 2. 構造函數
CallInst *inst = nullptr;
IRBuilder<> builder(&BB);
IRBuilder<> callBuilder(context);
Value *name = builder.CreateGlobalStringPtr(BB.getParent()->getName());
inst = callBuilder.CreateCall(beginFun, {name});

if (!inst) {
llvm::errs() << "Create First CallInst Failed\n";
return false;
}

// 3. 獲取函數開始的第一條指令
Instruction *beginInst = dyn_cast<Instruction>(BB.begin());

// 4. 將inst插入
inst->insertBefore(beginInst);

return true;
}

void insertEndInst(Function &F) {
LLVMContext &context = F.getParent()->getContext();
for (Function::iterator I = F.begin(), E = F.end(); I != E; ++I) {

// 函數結尾的BasicBlock
BasicBlock &BB = *I;
for (BasicBlock::iterator I = BB.begin(), E = BB.end(); I != E; ++I) {
ReturnInst *IST = dyn_cast<ReturnInst>(I);
if (!IST)
continue;

// end_func 類型
FunctionType *endFuncType = FunctionType::get(
Type::getVoidTy(context), {Type::getInt8PtrTy(context)}, false);

// end_func
FunctionCallee endFunc = BB.getModule()->getOrInsertFunction(
FunctionCallTimePass::EndFuncName, endFuncType);

// 構造end_func
IRBuilder<> builder(&BB);
IRBuilder<> callBuilder(context);
Value *name = builder.CreateGlobalStringPtr(BB.getParent()->getName());
CallInst *endCI = callBuilder.CreateCall(endFunc, {name});

// 插入end_func(struction)
endCI->insertBefore(IST);

errs() << "function-call-time: " << BB.getParent()->getName()
<< " end\n";
}
}
}
};
} // namespace

char FunctionCallTimePass::ID = 0;
StringRef FunctionCallTimePass::InsertFuncNamePrefix = "_kk_APT";
StringRef FunctionCallTimePass::BeginFuncName = "_kk_APTBeginSection";
StringRef FunctionCallTimePass::EndFuncName = "_kk_APTEndSection";
// 註冊給 opt
// opt -load LLVMFunctionCallTime.dylib -function-call-time xx.bc
static RegisterPass<FunctionCallTimePass>
X("function-call-time", "Function calls take time to collect", false,
false);
// 註冊給 clang 通過 -Xclang -load -Xclang LLVMFunctionCallTime.dylib
static RegisterStandardPasses Y(PassManagerBuilder::EP_EarlyAsPossible,
[](const PassManagerBuilder &Builder,
legacy::PassManagerBase &PM) {
PM.add(new FunctionCallTimePass());
});

創建Xcode工程

chmod +x ./build.sh # 只需要執行一次
./build.sh -b -x

執行成功後,會在LLVM目錄下生成build_dir目錄

build_dir
└── build_xcode
├── CMakeCache.txt
├── CMakeFiles
├── CMakeScripts
├── CPackConfig.cmake
├── CPackSourceConfig.cmake
├── Debug
├── LLVM.xcodeproj
├── LLVMBuild.cmake
├── MinSizeRel
├── RelWithDebInfo
├── Release
├── benchmarks
├── build
├── cmake
├── cmake_install.cmake
├── docs
├── examples
├── include
├── lib
├── llvm.spec
├── projects
├── runtimes
├── test
├── tools
├── unittests
└── utils

打開 LLVM.xcodeproj, 會提示創建scheme, 可以選擇自動,也可以選擇手動, Pass scheme 為LLVMFunctionCallTime

FunctionCallTime Pass target 在 Loadable modules 下面

選擇LLVMFunctionCallTime command + b 進行編譯

默認是Debug模式,所以LLVMFunctionCallTime.dylib產物在build_dir/build_xcode/Debug/lib/LLVMFunctionCallTime.dylib

使用 Pass

由於Xcode內置的clang,不支持加載自定義插件,所以直接從LLVM Project Github 倉庫下載編譯好的

由於開始下載的源碼為LLVM 11.0.0, 所以為了一致也是下載相同的版本

wget https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.0/clang+llvm-11.0.0-x86_64-apple-darwin.tar.xz
mkdir -p ./clang-11.0.0
tar xf clang+llvm-11.0.0-x86_64-apple-darwin.tar.xz -C ./clang-11.0.0 --strip-components 1

下載 everettjf/AppleTrace

將 appletrace.h appletrace.mm 拖入你的工程中

將APTBeginSection APTEndSection APTSyncWait 修改為 kkAPTBeginSection kkAPTEndSection kkAPTSyncWait,為了便於區分在Pass裡面指定了 kkAPT前綴

將appletrace.mm 中WriteSection 改為如下

void WriteSection(const char *name, const char *ph) {
pthread_t thread = pthread_self();
__uint64_t thread_id = 0;
pthread_threadid_np(thread, &thread_id);

uint64_t time = mach_absolute_time() * timeinfo_.numer / timeinfo_.denom;
uint64_t elapsed = (time - begin_) / 1000.0;

if (main_thread_id == 0 && pthread_main_np() != 0) {
main_thread_id = thread_id;
}

if (main_thread_id == thread_id) {
thread_id = 0; // just make main thread id zero
}

// 通過 llvm pass 插樁傳遞過來的 name 最前有個 \0x1 所以需要特殊處理下
// 當然也可以直接在 pass 中處理好
NSString *str = nil;
if (name[0] == '\x01') {
str = [NSString stringWithFormat:@"{\"name\":\"%s\",\"cat\":\"catname\",\"ph\":\"%s\",\"pid\":666,\"tid\":%llu,\"ts\":%llu}",
name + 1, ph, thread_id, elapsed];
} else {
str = [NSString stringWithFormat:@"{\"name\":\"%s\",\"cat\":\"catname\",\"ph\":\"%s\",\"pid\":666,\"tid\":%llu,\"ts\":%llu}",
name, ph, thread_id, elapsed];
}

dispatch_async(queue_, ^{
log_.AddLine(str.UTF8String);
});
}

在你的工程中創建一個xcconfig 文件,內容如下

//
// Config.xcconfig
// PageLinkDemo
//
// Created by king on 2020/12/18.
//

// Configuration settings file format documentation can be found at:
// https://help.apple.com/xcode/#/dev745c5c974

LLVM_DIR = $HOME/Documents/LLVM # 修改為在你本機的實際路徑
PASS_DYLIB = $(LLVM_DIR)/build_dir/build_xcode/Debug/lib/LLVMFunctionCallTime.dylib
OTHER_CFLAGS = $(inherited) -Xclang -load -Xclang $(PASS_DYLIB)
OTHER_CPLUSPLUSFLAGS = $(inherited) -Xclang -load -Xclang $(PASS_DYLIB)
COMPILER_INDEX_STORE_ENABLE = NO
CC = $(LLVM_DIR)/clang-11.0.0/bin/clang
CXX = $(LLVM_DIR)clang-11.0.0/bin/clang++

2020-12-20 14:49:32.530027+0800 qmuidemo[25684:1347559] log path = /Users/king/Library/Developer/CoreSimulator/Devices/71211D11-6169-4508-BBD8-B38958350C8F/data/Containers/Data/Application/46E933FE-D962-4355-9E0D-F8CBA44ABFA7/Library/appletracedata/trace.appletrace

使用everettjf/AppleTrace生成火焰圖

通過上面輸出的log path,將appletracedata目錄拷貝到 下載的everettjf/AppleTrace中

將下載的everettjf/AppleTrace merge.py 中 run函數的while循環外面添加如下代碼

self.output.seek(-2, os.SEEK_END)
self.output.truncate()
self.output.write('\n]')

python merge.py -d ./appletracedata

sh get_catapult.sh

# 生成
python catapult/tracing/bin/trace2html appletracedata/trace.json --output=appletracedata/trace.html
# 打開預覽
open trace.html

使用Xcode 調試 Pass

需要通過opt調試

首先需要先編譯好Pass

生成IR文件

clang -x objective-c++ -isysroot $(xcrun --sdk macosx --show-sdk-path) -S -emit-llvm -c xxx.m -o xx.ll

切換為opt scheme, 選擇Edit Scheme 設置 opt的啟動參數,如下

然後運行 opt, 由於opt依賴很多,所有需要編譯很長時間,不過如果你是40萬的Mac Pro,那麼可能只需要幾秒或者十幾秒

這樣就可以直接在Pass源碼中下斷點

LLVMFunctionCallTimePass 源碼

參考

Writing an LLVM Pass
Clang 插件統計方法耗時

就差您點一下了 👇👇👇

相關焦點

  • 自定義 LLVM PASS 實現函數耗時插樁統計
    最近一位優秀的群友,使用LLVM Pass實現了「編譯時插樁」的方法。文章地址:https://blog.0x1306a94.com/docs/llvm/ch01/01/源碼地址:https://github.com/0x1306a94/LLVMFunctionCallTimePass原理原理很簡單
  • LLVM 初探
    (Backend) - 根據中間語言,生成對應的 CPU 架構指令 例如 X86,ARM;通過這種設計,增加新的語言,只需要實現新的 Frontend,Optimizer 和 Backend 可以重用;同理新增新的 CPU 架構時,也只需要實現新的 Backend。
  • 關於LLVM,這些東西你必須要知道!
    當編譯器需要支持多種原始碼和目標架構時,基於LLVM的架構,設計一門新的語言只需要去實現一個新的前端就行了,支持新的後端架構也只需要實現一個新的後端就行了。其它部分完成可以復用,就不用再重新設計一次了。
  • 使用ollvm自定義簡單的字符串加密
    題目主要是為了能熟練ollvm中如何進行一個簡單的加密,以及c++部分怎麼生成對應的IR指令來達到像c++函數效果。所以主要我們的思路可以切換成:2、根據C++的算法。生成一份IR指令來為我們提供參考4、根據參考的IR指令來生成我們需要的加密和解密函數做完這個題目,基本就可以對ollvm的工作原理有一定的了解,並且改造屬於自己的加密或者混淆了。
  • VBA自定義函數
    '1 什麼是自定義函數? '在VBA中有VBA函數,我們還可以調用工作表函數,我們能不能自已編寫函數呢?
  • python--定義函數
    在Python中,定義一個函數要使用def語句,依次寫出函數名、括號、括號中的參數和冒號:,然後,在縮進塊中編寫函數體,函數的返回值用return語句返回。下面自定義一個求絕對值的my_abs函數為例:def my_abs(x):    if x >= 0:        return x    else:        return -x如果沒有return語句,函數執行完畢後也會返回結果,只是結果為None。return None可以簡寫為return。
  • 【VBA自定義函數】315個自定義函數
    166、工齡計算:167、計算日期差,除去星期六、星期日的自定義函數168、這是一個將英文字反轉的自定函數169、關於個人所得稅的170、一個能計算是否有重複單元的函數171、試編寫數字金額轉中文大寫的函數172、人民幣大小寫轉換函數173、獲取區域顏色值自定義函數174、獲取活動工作表名的自定義函數175、顯示在「插入函數」對話框的「或選擇類別」下拉列表中176、複合函數STATFUNCTION177
  • Excel函數應用篇:進行自定義函數的設置技巧
    今天,就教大家在Excel中進行自定義函數的設置技巧。  Excel中進行自定義函數的設置步驟  下面通過一個例子來學習簡單的編寫自定義函數  例:下面表格中需要計算一些三角形的面積  B列是底邊長,C列是高,要求在D列通過公式計算三角形面積。
  • LAMBDA函數,讓EXCEL自定義函數告別VBA
    一提起自定義函數(UDF),很多表哥表姐想到的Alt+F11打開VBE編輯器,寫一個Function.隨著EXCEL版本的更新,EXCEL也像其它程式語言一樣,推出了表達式函數LAMBDA,通過這個函數加名稱管理器,我們可以在編寫簡短的自定義函數時,不再需要使用VBE窗體。
  • 一個簡單例子說明php自定義函數過濾字符串功能實現!
    為了避免用戶在網站上發布違規違法內容,網站開發人員為了規範網站內容採取了過濾字符串的手段,今天為大家講解一下自定義函數過濾字符串。實現過程1、製作用戶評論文本框和評論提交按鈕。1、創建php文件。2、定義具有過濾功能的驗證函數,設定過濾規則。3、調用函數去判斷用戶提交內容是否符合規則。代碼如下:PHP編程實例:簡單的自定義函數過濾字符串功能實現!
  • 【線上問題定位】Llvm庫ARM環境崩潰問題
    源碼」問題現象本地編寫的demo, 在多線程組裝和運行JIT函數的過程中產生崩潰問題。跑產品集成測試環境是1000並發,每一個並發線程組裝和運行JIT函數100次。是否可以提取成demo, 降低複雜度?把JIT相關組裝和使用的簡化邏輯提取出來編寫成demo,並且模擬環境1000並發,組裝JIT函數100次的場景。問題得到復現。崩潰的位置,處理的是什麼邏輯?
  • 【Matlab基礎】 自定義函數
    前言函數——是編程的核心概念之一,是能夠完成相對獨立功能的代碼封裝成的模塊。在主程序中通過函數名和實參調用它,通過接口(即函數的輸入、輸出參數)來實現「通訊」。所以在調用函數時,你只要知道「被調用的函數是用來做什麼的」,以及「如何對應它的輸入、輸出參數」就行了。
  • 帶UI界面的代碼統計小工具--進程、隊列的並發應用
    創建多個進程,從隊列中獲取文件路徑,分別統計各類型文件的代碼行數。3.    將統計的結果,存放到一個變量裡,並且實現多進程間變量共享。4.    製作一個圖形界面,用於選擇文件夾和統計代碼,並展示統計結果。
  • Excel中如何添加自定義函數到函數庫
    那麼該如何進行自定義函數,自定義的函數如何添加到函數庫,並對其添加必要的使用說明,使其更像內置函數呢?下面以自定義WLOOKUP函數為例,為讀者詳細講解。WLOOKUP自定義函數其實是INDEX和MATCH函數嵌套函數,實現的是查找匹配值功能,與微軟新出的XLOOKUP函數功能一致,但XLOOKUP函數只有Office 365和Excel2019版本中有,所以自定義WLOOKUP函數主要是為低版本Excel提供XLOOKUP函數功能。
  • PHP編程實例:自定義函數實現簡單數字加密和解密算法
    實現過程1、製作form表單提交框一個文本輸入框和一個提交按鈕。2、創建php腳本文件。3、定義加密數字和解密數字的函數。4、調用自定義函數處理用戶輸入的數據,輸出加密數字和解密數字。2、$_POST預定義數組$_POST在PHP中是預定義數組,它屬於外部變量.它的作用收集用戶通過表單提交數據,$_POST收集的數據可以賦給變量再進一步處理,上面代碼就是把$_POST賦給變量作為自定義函數的參數。
  • 如何在Keras中創建自定義損失函數?
    Keras 中的自定義損失函數可以以我們想要的方式提高機器學習模型的性能,並且對於更有效地解決特定問題非常有用。例如,假設我們正在構建一個股票投資組合優化模型。在這種情況下,設計一個定製損失函數將有助於實現對在錯誤方向上預測價格變動的巨大懲罰。
  • C語言編程技巧:控制臺程序中自定義函數實現數組內容的特定顯示
    因此,我們可以寫一個自定義的通用函數,用來根據需要顯示數組中的內容,並且可以自定義數據的顯示格式,如設置每行顯示的數據點數,設置位寬、保留小數點位數等。針對這種情況,下面給出這種自定義函數的編程實現方法。
  • 【python基礎】python自定義函數五種用法
    Python自定義函數是以def開頭,空一格之後是這個自定義函數的名稱,名稱後面是一對括號,括號裡放置形參列表,結束括號後面一定要有冒號「:」,
  • python 函數定義以及函數的調用
    函數的定義1. 函數的定義的格式def 函數名(參數):passreturn 表達式2.函數的調用1. 函數參數函數定義之後,那函數裡面可以傳入哪些對象呢?第一種 必備參數:def func(x):pass第二種 默認參數:def func(x, y=None):pass
  • EXCEL 小試牛刀 自定義函數能夠完成的 數組函數也可以完成
    有這麼一個表,就是一些成績,分數:然後要求就來了,問統計一下不合格人員的名單,按性別和課程進行統計,統計的結果每個人名之間以逗號間隔,結果參照下方所示:猛地一看,這完全不符合EXCEL規範,寫公式將文本連結?