@netoalmanca/advpl-sensei 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents/changelog-generator.md +63 -0
- package/agents/code-generator.md +215 -0
- package/agents/code-reviewer.md +145 -0
- package/agents/debugger.md +83 -0
- package/agents/doc-generator.md +67 -0
- package/agents/docs-reference.md +86 -0
- package/agents/migrator.md +84 -0
- package/agents/process-consultant.md +97 -0
- package/agents/refactorer.md +75 -0
- package/agents/sx-configurator.md +67 -0
- package/commands/changelog.md +66 -0
- package/commands/diagnose.md +67 -0
- package/commands/docs.md +81 -0
- package/commands/document.md +67 -0
- package/commands/explain.md +60 -0
- package/commands/generate.md +111 -0
- package/commands/migrate.md +81 -0
- package/commands/process.md +111 -0
- package/commands/refactor.md +65 -0
- package/commands/review.md +60 -0
- package/commands/sxgen.md +98 -0
- package/commands/test.md +103 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +143 -0
- package/dist/index.js.map +1 -0
- package/package.json +30 -0
- package/skills/advpl-code-generation/SKILL.md +163 -0
- package/skills/advpl-code-generation/patterns-fwformbrowse.md +485 -0
- package/skills/advpl-code-generation/patterns-jobs.md +519 -0
- package/skills/advpl-code-generation/patterns-mvc.md +765 -0
- package/skills/advpl-code-generation/patterns-pontos-entrada.md +708 -0
- package/skills/advpl-code-generation/patterns-rest.md +974 -0
- package/skills/advpl-code-generation/patterns-soap.md +639 -0
- package/skills/advpl-code-generation/patterns-treport.md +481 -0
- package/skills/advpl-code-generation/patterns-workflow.md +779 -0
- package/skills/advpl-code-generation/templates-classes.md +1096 -0
- package/skills/advpl-code-review/SKILL.md +72 -0
- package/skills/advpl-code-review/rules-best-practices.md +444 -0
- package/skills/advpl-code-review/rules-modernization.md +290 -0
- package/skills/advpl-code-review/rules-performance.md +333 -0
- package/skills/advpl-code-review/rules-security.md +302 -0
- package/skills/advpl-debugging/SKILL.md +265 -0
- package/skills/advpl-debugging/common-errors.md +1124 -0
- package/skills/advpl-debugging/performance-tips.md +768 -0
- package/skills/advpl-refactoring/SKILL.md +139 -0
- package/skills/advpl-to-tlpp-migration/SKILL.md +293 -0
- package/skills/advpl-to-tlpp-migration/migration-checklist.md +122 -0
- package/skills/advpl-to-tlpp-migration/migration-rules.md +265 -0
- package/skills/changelog-patterns/SKILL.md +99 -0
- package/skills/code-explanation/SKILL.md +66 -0
- package/skills/documentation-patterns/SKILL.md +172 -0
- package/skills/embedded-sql/SKILL.md +379 -0
- package/skills/probat-testing/SKILL.md +226 -0
- package/skills/probat-testing/patterns-unit-tests.md +614 -0
- package/skills/protheus-business/SKILL.md +92 -0
- package/skills/protheus-business/modulo-compras.md +780 -0
- package/skills/protheus-business/modulo-contabilidade.md +874 -0
- package/skills/protheus-business/modulo-estoque.md +876 -0
- package/skills/protheus-business/modulo-faturamento.md +800 -0
- package/skills/protheus-business/modulo-financeiro.md +1015 -0
- package/skills/protheus-business/modulo-fiscal.md +749 -0
- package/skills/protheus-business/modulo-manutencao.md +848 -0
- package/skills/protheus-business/modulo-pcp.md +743 -0
- package/skills/protheus-reference/SKILL.md +119 -0
- package/skills/protheus-reference/native-functions.md +7029 -0
- package/skills/protheus-reference/rest-api-reference.md +1758 -0
- package/skills/protheus-reference/restricted-functions.md +265 -0
- package/skills/protheus-reference/sx-dictionary.md +854 -0
- package/skills/sx-configuration/SKILL.md +184 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
# Security Rules
|
|
2
|
+
|
|
3
|
+
Rules for detecting security vulnerabilities in ADVPL/TLPP code. Each rule includes a detection pattern, violation example, correct example, and explanation.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## [SEC-001] SQL built by string concatenation without %exp: macro (SQL injection risk)
|
|
8
|
+
|
|
9
|
+
**Severity:** CRITICAL
|
|
10
|
+
|
|
11
|
+
**Description:** Building SQL queries by concatenating user-supplied or external variables directly into the query string allows SQL injection attacks. Attackers can manipulate input to execute arbitrary SQL commands, potentially reading, modifying, or deleting data.
|
|
12
|
+
|
|
13
|
+
**What to look for:** `TCQuery` or string-based SQL construction where variables are concatenated with `+` instead of using BeginSQL with `%exp:variable%` macro substitution.
|
|
14
|
+
|
|
15
|
+
**Violation:**
|
|
16
|
+
|
|
17
|
+
```advpl
|
|
18
|
+
User Function SearchClient(cSearch)
|
|
19
|
+
Local cQuery := ""
|
|
20
|
+
|
|
21
|
+
// DANGEROUS: cSearch is concatenated directly into SQL
|
|
22
|
+
cQuery := "SELECT A1_COD, A1_NOME FROM " + RetSqlName("SA1")
|
|
23
|
+
cQuery += " WHERE A1_NOME LIKE '%" + cSearch + "%'"
|
|
24
|
+
cQuery += " AND D_E_L_E_T_ = ' '"
|
|
25
|
+
|
|
26
|
+
TCQuery cQuery New Alias "QRY_CLI"
|
|
27
|
+
|
|
28
|
+
// If cSearch = "'; DROP TABLE SA1; --"
|
|
29
|
+
// the query becomes destructive!
|
|
30
|
+
|
|
31
|
+
Return
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Correct:**
|
|
35
|
+
|
|
36
|
+
```advpl
|
|
37
|
+
User Function SearchClient(cSearch)
|
|
38
|
+
Local cAlias := GetNextAlias()
|
|
39
|
+
Local cFilter := "%" + cSearch + "%"
|
|
40
|
+
|
|
41
|
+
BeginSQL Alias cAlias
|
|
42
|
+
SELECT SA1.A1_COD, SA1.A1_NOME
|
|
43
|
+
FROM %table:SA1% SA1
|
|
44
|
+
WHERE SA1.%notDel%
|
|
45
|
+
AND SA1.A1_FILIAL = %xfilial:SA1%
|
|
46
|
+
AND SA1.A1_NOME LIKE %exp:cFilter%
|
|
47
|
+
EndSQL
|
|
48
|
+
|
|
49
|
+
Return
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Why it matters:** SQL injection is one of the most exploited vulnerabilities in web applications and internal systems alike. In Protheus, an injected query could bypass access controls, extract sensitive financial data, or corrupt critical business tables. The `%exp:` macro safely parameterizes values, preventing injection.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## [SEC-002] REST endpoint using SELF:GetContent() without input validation
|
|
57
|
+
|
|
58
|
+
**Severity:** CRITICAL
|
|
59
|
+
|
|
60
|
+
**Description:** REST API endpoints that read request body content via `SELF:GetContent()` (or `oRest:GetBody()`) and use the data directly without validation expose the system to injection attacks, type errors, and business logic bypass.
|
|
61
|
+
|
|
62
|
+
**What to look for:** `SELF:GetContent()`, `oRest:GetBody()`, or similar request body reading methods where the returned content is used directly in SQL queries, database operations, or business logic without validation or sanitization.
|
|
63
|
+
|
|
64
|
+
**Violation:**
|
|
65
|
+
|
|
66
|
+
```tlpp
|
|
67
|
+
@Get("/api/v1/clients")
|
|
68
|
+
Method GetClient() Class ClientService
|
|
69
|
+
Local cBody := SELF:GetContent()
|
|
70
|
+
Local jBody := JsonObject():New()
|
|
71
|
+
Local cCodCli := ""
|
|
72
|
+
|
|
73
|
+
jBody:FromJson(cBody)
|
|
74
|
+
cCodCli := jBody["code"] // No validation!
|
|
75
|
+
|
|
76
|
+
// Used directly in database lookup
|
|
77
|
+
DbSelectArea("SA1")
|
|
78
|
+
DbSetOrder(1)
|
|
79
|
+
DbSeek(xFilial("SA1") + cCodCli)
|
|
80
|
+
|
|
81
|
+
// cCodCli could be NIL, empty, oversized, or contain
|
|
82
|
+
// malicious data - no checks performed
|
|
83
|
+
|
|
84
|
+
Return
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Correct:**
|
|
88
|
+
|
|
89
|
+
```tlpp
|
|
90
|
+
@Get("/api/v1/clients")
|
|
91
|
+
Method GetClient() Class ClientService
|
|
92
|
+
Local cBody := SELF:GetContent()
|
|
93
|
+
Local jBody := JsonObject():New()
|
|
94
|
+
Local cCodCli := ""
|
|
95
|
+
Local nRet := jBody:FromJson(cBody)
|
|
96
|
+
|
|
97
|
+
// Validate JSON parsing
|
|
98
|
+
If nRet <> 0
|
|
99
|
+
SELF:SetResponse('{"error": "Invalid JSON body"}')
|
|
100
|
+
SELF:SetStatus(400)
|
|
101
|
+
Return
|
|
102
|
+
EndIf
|
|
103
|
+
|
|
104
|
+
// Validate required field existence and type
|
|
105
|
+
If ValType(jBody["code"]) <> "C" .Or. Empty(jBody["code"])
|
|
106
|
+
SELF:SetResponse('{"error": "Field code is required and must be a string"}')
|
|
107
|
+
SELF:SetStatus(400)
|
|
108
|
+
Return
|
|
109
|
+
EndIf
|
|
110
|
+
|
|
111
|
+
// Validate field length and content
|
|
112
|
+
cCodCli := PadR(AllTrim(jBody["code"]), TamSX3("A1_COD")[1])
|
|
113
|
+
|
|
114
|
+
DbSelectArea("SA1")
|
|
115
|
+
DbSetOrder(1)
|
|
116
|
+
If DbSeek(xFilial("SA1") + cCodCli)
|
|
117
|
+
// Process valid client
|
|
118
|
+
Else
|
|
119
|
+
SELF:SetResponse('{"error": "Client not found"}')
|
|
120
|
+
SELF:SetStatus(404)
|
|
121
|
+
EndIf
|
|
122
|
+
|
|
123
|
+
Return
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Why it matters:** REST endpoints are exposed to external consumers and potentially the public internet. Without input validation, attackers can send malformed payloads that cause runtime errors (crashing the thread), bypass business rules, or exploit downstream operations. Every input must be validated for type, length, format, and business constraints.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## [SEC-003] Sensitive data in ConOut/FWLogMsg output
|
|
131
|
+
|
|
132
|
+
**Severity:** WARNING
|
|
133
|
+
|
|
134
|
+
**Description:** Logging sensitive information such as passwords, API tokens, CPF/CNPJ numbers, credit card numbers, or personal data in `ConOut` or `FWLogMsg` exposes it to anyone with access to the AppServer console or log files.
|
|
135
|
+
|
|
136
|
+
**What to look for:** `ConOut` or `FWLogMsg` calls that output variables containing passwords, tokens, credentials, CPF, CNPJ, or any personally identifiable information (PII).
|
|
137
|
+
|
|
138
|
+
**Violation:**
|
|
139
|
+
|
|
140
|
+
```advpl
|
|
141
|
+
User Function AuthenticateUser(cUser, cPassword)
|
|
142
|
+
Local lOk := .F.
|
|
143
|
+
|
|
144
|
+
// DANGER: password logged in plain text!
|
|
145
|
+
ConOut("[Auth] Attempting login - User: " + cUser + " Password: " + cPassword)
|
|
146
|
+
|
|
147
|
+
lOk := ValidateCredentials(cUser, cPassword)
|
|
148
|
+
|
|
149
|
+
If !lOk
|
|
150
|
+
FWLogMsg("WARNING", , "AUTH", "Security", "", 01, ;
|
|
151
|
+
"Failed login for user: " + cUser + " with password: " + cPassword)
|
|
152
|
+
EndIf
|
|
153
|
+
|
|
154
|
+
Return lOk
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Correct:**
|
|
158
|
+
|
|
159
|
+
```advpl
|
|
160
|
+
User Function AuthenticateUser(cUser, cPassword)
|
|
161
|
+
Local lOk := .F.
|
|
162
|
+
|
|
163
|
+
ConOut("[Auth] Attempting login - User: " + cUser)
|
|
164
|
+
|
|
165
|
+
lOk := ValidateCredentials(cUser, cPassword)
|
|
166
|
+
|
|
167
|
+
If !lOk
|
|
168
|
+
FWLogMsg("WARNING", , "AUTH", "Security", "", 01, ;
|
|
169
|
+
"Failed login attempt for user: " + cUser)
|
|
170
|
+
EndIf
|
|
171
|
+
|
|
172
|
+
Return lOk
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Why it matters:** Log files are often stored unencrypted, shared across teams, and retained for long periods. Sensitive data in logs can be accessed by unauthorized personnel, leaked through log aggregation tools, or exposed in security breaches. Regulations like LGPD (Brazil) and GDPR explicitly require that personal data not be logged unnecessarily.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## [SEC-004] Hardcoded credentials in source code
|
|
180
|
+
|
|
181
|
+
**Severity:** WARNING
|
|
182
|
+
|
|
183
|
+
**Description:** Passwords, API keys, database connection strings, and other credentials must never be hardcoded in source code. Source code is stored in version control, compiled into RPO, and accessible to all developers, making hardcoded secrets a significant security risk.
|
|
184
|
+
|
|
185
|
+
**What to look for:** String literals that appear to be passwords, API keys, bearer tokens, connection strings, or authentication secrets assigned to variables or used directly in function calls.
|
|
186
|
+
|
|
187
|
+
**Violation:**
|
|
188
|
+
|
|
189
|
+
```advpl
|
|
190
|
+
User Function ConnectAPI()
|
|
191
|
+
Local cUrl := "https://api.example.com/v1"
|
|
192
|
+
Local cApiKey := "sk-abc123def456ghi789jkl012mno345"
|
|
193
|
+
Local cPass := "P@ssw0rd!2025"
|
|
194
|
+
Local aHeaders := {}
|
|
195
|
+
|
|
196
|
+
aAdd(aHeaders, "Authorization: Bearer " + cApiKey)
|
|
197
|
+
aAdd(aHeaders, "Content-Type: application/json")
|
|
198
|
+
|
|
199
|
+
cResponse := HttpGet(cUrl + "/data", "", aHeaders)
|
|
200
|
+
|
|
201
|
+
Return cResponse
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Correct:**
|
|
205
|
+
|
|
206
|
+
```advpl
|
|
207
|
+
User Function ConnectAPI()
|
|
208
|
+
Local cUrl := "https://api.example.com/v1"
|
|
209
|
+
Local cApiKey := GetAPICredential("EXAMPLE_API_KEY")
|
|
210
|
+
Local aHeaders := {}
|
|
211
|
+
|
|
212
|
+
If Empty(cApiKey)
|
|
213
|
+
ConOut("[ConnectAPI] API key not configured. Check SX6 parameter EXAMPLE_API_KEY.")
|
|
214
|
+
Return ""
|
|
215
|
+
EndIf
|
|
216
|
+
|
|
217
|
+
aAdd(aHeaders, "Authorization: Bearer " + cApiKey)
|
|
218
|
+
aAdd(aHeaders, "Content-Type: application/json")
|
|
219
|
+
|
|
220
|
+
cResponse := HttpGet(cUrl + "/data", "", aHeaders)
|
|
221
|
+
|
|
222
|
+
Return cResponse
|
|
223
|
+
|
|
224
|
+
// Store credentials in SX6 parameters, environment variables,
|
|
225
|
+
// or a secure vault - never in source code
|
|
226
|
+
Static Function GetAPICredential(cParamName)
|
|
227
|
+
Local cValue := AllTrim(GetMV(cParamName))
|
|
228
|
+
Return cValue
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Why it matters:** Hardcoded credentials in source code are exposed to every developer with repository access, remain in version history even after deletion, and can be extracted from compiled RPO. If the repository is compromised, all hardcoded credentials are immediately exposed. Use Protheus SX6 parameters, environment variables, or a secrets management solution instead.
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## [SEC-005] Usage of TOTVS restricted/internal functions, classes, or variables
|
|
236
|
+
|
|
237
|
+
**Severity:** CRITICAL
|
|
238
|
+
|
|
239
|
+
**Description:** TOTVS maintains a list of functions, classes, and variables that are internal property. These resources are NOT documented, NOT supported, and may be altered or removed without notice. Some have their compilation blocked since release 12.1.33. Using them in custom code causes compilation failures, runtime errors, or unpredictable behavior after updates.
|
|
240
|
+
|
|
241
|
+
**What to look for:** Any call to functions or classes listed in the TOTVS restricted resources list. See `restricted-functions.md` in the protheus-reference skill for the complete list.
|
|
242
|
+
|
|
243
|
+
**Compilation BLOCKED (will not compile on 12.1.33+):**
|
|
244
|
+
|
|
245
|
+
- `StaticCall()` — use public User Functions or TLPP namespaced calls instead
|
|
246
|
+
- `PTInternal()` — no replacement available; redesign the logic
|
|
247
|
+
|
|
248
|
+
**Commonly found restricted functions (NOT supported, may change without notice):**
|
|
249
|
+
|
|
250
|
+
| Restricted | Alternative |
|
|
251
|
+
|------------|-------------|
|
|
252
|
+
| `PARAMBOX` | Use `Pergunte()` with SX1 or `FWInputDialog()` |
|
|
253
|
+
| `MsExcel` | Use `FWMsExcel` or `FWAdaptor` |
|
|
254
|
+
| `CriaVar` | Use `FWCriaVar()` |
|
|
255
|
+
| `LASTKEY` | Use specific navigation control logic |
|
|
256
|
+
| `SetPrvt` | Declare variables explicitly with `Private` |
|
|
257
|
+
| `MontaBlock` | Build code blocks directly with `{|| ... }` |
|
|
258
|
+
| `FWMVCROTAUTO` | Use `FWExecView()` for MVC automation |
|
|
259
|
+
| `FWLOOKUP` | Use `FWInputDialog()` or standard F3 lookups |
|
|
260
|
+
| `OPENSXS` | Tables are opened automatically by the framework |
|
|
261
|
+
| `APPEND FROM` | Use `RecLock` + field-by-field copy |
|
|
262
|
+
| `COPY TO` | Use `RecLock` + field-by-field copy |
|
|
263
|
+
| `LoadLayout()` | Use `FWLoadLayout()` |
|
|
264
|
+
| `PivotTable` | Use `FWPivotTable` |
|
|
265
|
+
| `StaticCall()` | Use `User Function` (public) or TLPP namespace |
|
|
266
|
+
| `ApOleClient` | Use `FWMsExcel` for Excel or native file operations |
|
|
267
|
+
| `FWAUTHUSER` | Use proper REST authentication mechanisms |
|
|
268
|
+
| `E_FIELD` | Use `GetSx3Cache()` or `TamSx3()` |
|
|
269
|
+
|
|
270
|
+
**Violation:**
|
|
271
|
+
|
|
272
|
+
```advpl
|
|
273
|
+
User Function MyReport()
|
|
274
|
+
// WRONG: StaticCall is BLOCKED since 12.1.33
|
|
275
|
+
Local aMenu := StaticCall(MATA030, MenuDef)
|
|
276
|
+
|
|
277
|
+
// WRONG: PARAMBOX is restricted
|
|
278
|
+
PARAMBOX(aParams, "Filtros", , , , , , , , , .F., .F.)
|
|
279
|
+
|
|
280
|
+
// WRONG: CriaVar is restricted
|
|
281
|
+
Local xVal := CriaVar("A1_COD")
|
|
282
|
+
Return
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**Correct:**
|
|
286
|
+
|
|
287
|
+
```advpl
|
|
288
|
+
User Function MyReport()
|
|
289
|
+
// CORRECT: Call public function directly
|
|
290
|
+
Local aMenu := U_MenuDef()
|
|
291
|
+
|
|
292
|
+
// CORRECT: Use Pergunte() with SX1 configuration
|
|
293
|
+
Pergunte("MYREPORT", .F.)
|
|
294
|
+
|
|
295
|
+
// CORRECT: Use FWCriaVar
|
|
296
|
+
Local xVal := FWCriaVar("A1_COD")
|
|
297
|
+
Return
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
**Reference:** Full list at https://centraldeatendimento.totvs.com/hc/pt-br/articles/360016461772
|
|
301
|
+
|
|
302
|
+
**Why it matters:** TOTVS reserves the right to modify or remove internal functions without notice. Code that uses restricted functions will break silently after Protheus updates — or fail to compile entirely on releases 12.1.33+. The TOTVS support team does NOT provide assistance for issues caused by restricted function usage. Always use documented, supported alternatives.
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: advpl-debugging
|
|
3
|
+
description: Use when debugging ADVPL/TLPP errors, analyzing Protheus logs, diagnosing performance issues, or troubleshooting compilation and runtime problems
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ADVPL/TLPP Debugging
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Systematic methodology for diagnosing and resolving errors in ADVPL/TLPP on TOTVS Protheus. This skill covers compilation errors, runtime failures, performance bottlenecks, database locks, log analysis, and AppServer troubleshooting.
|
|
11
|
+
|
|
12
|
+
## When to Use
|
|
13
|
+
|
|
14
|
+
- Compilation errors (syntax, missing includes, undeclared variables)
|
|
15
|
+
- Runtime errors (NIL access, type mismatch, array bounds)
|
|
16
|
+
- Performance issues (slow queries, memory leaks, excessive loops)
|
|
17
|
+
- Database locks (RecLock timeouts, deadlocks, exclusive access failures)
|
|
18
|
+
- Log analysis (Protheus console, AppServer logs, SmartClient logs)
|
|
19
|
+
- AppServer issues (crash, high memory, thread exhaustion, connection problems)
|
|
20
|
+
|
|
21
|
+
## Debug Methodology
|
|
22
|
+
|
|
23
|
+
```dot
|
|
24
|
+
digraph debug_flow {
|
|
25
|
+
rankdir=TB;
|
|
26
|
+
node [shape=box, style=rounded];
|
|
27
|
+
|
|
28
|
+
A [label="Error Reported"];
|
|
29
|
+
B [label="Compilation Error?" shape=diamond];
|
|
30
|
+
C [label="Check common-errors.md\nFix syntax/includes/declarations"];
|
|
31
|
+
D [label="Add Logging\n(Conout / FWLogMsg)"];
|
|
32
|
+
E [label="Reproduce Error"];
|
|
33
|
+
F [label="Analyze Stack Trace\n& Log Output"];
|
|
34
|
+
G [label="Identify Root Cause"];
|
|
35
|
+
H [label="Apply Fix"];
|
|
36
|
+
I [label="Validate Fix\n& Remove Debug Logging"];
|
|
37
|
+
|
|
38
|
+
A -> B;
|
|
39
|
+
B -> C [label="Yes"];
|
|
40
|
+
B -> D [label="No (Runtime)"];
|
|
41
|
+
C -> I [label="Fixed"];
|
|
42
|
+
D -> E;
|
|
43
|
+
E -> F;
|
|
44
|
+
F -> G;
|
|
45
|
+
G -> H;
|
|
46
|
+
H -> I;
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Steps:**
|
|
51
|
+
|
|
52
|
+
1. **Error reported** - Collect the error message, stack trace, and reproduction steps
|
|
53
|
+
2. **Compilation?** - If yes, consult `common-errors.md` for immediate fix
|
|
54
|
+
3. **Add logging** - Insert Conout/FWLogMsg at strategic points around the failure
|
|
55
|
+
4. **Reproduce** - Recreate the error in a controlled environment
|
|
56
|
+
5. **Analyze stack** - Read the full stack trace, identify the failing line and function
|
|
57
|
+
6. **Identify root cause** - Determine why the error occurs (data, logic, environment)
|
|
58
|
+
7. **Fix** - Apply the correction, validate, and remove debug logging
|
|
59
|
+
|
|
60
|
+
## Quick Diagnosis by Error Type
|
|
61
|
+
|
|
62
|
+
| Symptom | Likely Cause | First Check |
|
|
63
|
+
|---------|-------------|-------------|
|
|
64
|
+
| Variable does not exist | Undeclared Local or typo in name | Verify variable declaration matches usage (case-sensitive) |
|
|
65
|
+
| Array access out of bounds | Index exceeds `Len(aArray)` or index <= 0 | Add `Conout(Len(aArray))` before the access line |
|
|
66
|
+
| Type mismatch on operation | Operating on NIL or wrong type | Check `ValType()` of both operands before the line |
|
|
67
|
+
| File not found / Include error | Missing .ch file or wrong RPO | Verify `#Include` paths and RPO compilation |
|
|
68
|
+
| Lock timeout (RecLock) | Record locked by another user/thread | Check `RecLock(cAlias, .F.)` with timeout, use SM0/SMA lock monitor |
|
|
69
|
+
| Memory allocation error | Array growing unbounded or object leak | Check loops for `aAdd` without limit, verify `FreeObj()` calls |
|
|
70
|
+
| Invalid alias XXXX | WorkArea not opened or closed prematurely | Verify `DbSelectArea()` precedes the alias usage, check `Select(cAlias) > 0` |
|
|
71
|
+
|
|
72
|
+
## Logging Tools
|
|
73
|
+
|
|
74
|
+
### Conout (Console Output)
|
|
75
|
+
|
|
76
|
+
Simple console logging. Output appears in the Protheus AppServer console.
|
|
77
|
+
|
|
78
|
+
```advpl
|
|
79
|
+
// Basic variable inspection
|
|
80
|
+
Conout(">>> [FATA001] cCodCli: " + cValToChar(cCodCli))
|
|
81
|
+
Conout(">>> [FATA001] nTotal: " + cValToChar(nTotal))
|
|
82
|
+
Conout(">>> [FATA001] ValType(xRet): " + ValType(xRet))
|
|
83
|
+
|
|
84
|
+
// Array inspection
|
|
85
|
+
Conout(">>> aItens length: " + cValToChar(Len(aItens)))
|
|
86
|
+
If Len(aItens) > 0
|
|
87
|
+
Conout(">>> aItens[1]: " + cValToChar(aItens[1]))
|
|
88
|
+
EndIf
|
|
89
|
+
|
|
90
|
+
// Flow tracing
|
|
91
|
+
Conout(">>> Entering FATA001 at " + Time())
|
|
92
|
+
// ... code ...
|
|
93
|
+
Conout(">>> Leaving FATA001 at " + Time())
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### FWLogMsg (Structured Logging - Preferred)
|
|
97
|
+
|
|
98
|
+
Structured logging with severity levels. Logs are stored in the Protheus log system and can be queried.
|
|
99
|
+
|
|
100
|
+
```advpl
|
|
101
|
+
// Severity levels: "INFO", "WARNING", "ERROR"
|
|
102
|
+
FWLogMsg("INFO", , "FATA001", "MyModule", "", 01, ;
|
|
103
|
+
"Processing started for client: " + cCodCli)
|
|
104
|
+
|
|
105
|
+
FWLogMsg("ERROR", , "FATA001", "MyModule", "", 02, ;
|
|
106
|
+
"Failed to lock record SA1. Alias: " + cAlias + ;
|
|
107
|
+
" RecNo: " + cValToChar(RecNo()))
|
|
108
|
+
|
|
109
|
+
FWLogMsg("WARNING", , "FATA001", "MyModule", "", 03, ;
|
|
110
|
+
"Slow query detected. Elapsed: " + cValToChar(nElapsed) + "s")
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### ErrorBlock for Custom Stack Trace Capture
|
|
114
|
+
|
|
115
|
+
Capture and log full error details including the call stack.
|
|
116
|
+
|
|
117
|
+
```advpl
|
|
118
|
+
Local oError
|
|
119
|
+
Local bOldError := ErrorBlock({|e| oError := e, Break(e)})
|
|
120
|
+
|
|
121
|
+
Begin Sequence
|
|
122
|
+
|
|
123
|
+
// Code that may fail
|
|
124
|
+
DbSelectArea("SA1")
|
|
125
|
+
DbSetOrder(1)
|
|
126
|
+
DbSeek(xFilial("SA1") + cCodCli)
|
|
127
|
+
cNome := SA1->A1_NOME
|
|
128
|
+
|
|
129
|
+
Recover Using oError
|
|
130
|
+
Conout("=== ERROR CAPTURED ===")
|
|
131
|
+
Conout("Description: " + oError:Description)
|
|
132
|
+
Conout("GenCode: " + cValToChar(oError:GenCode))
|
|
133
|
+
Conout("SubCode: " + cValToChar(oError:SubCode))
|
|
134
|
+
Conout("OsCode: " + cValToChar(oError:OsCode))
|
|
135
|
+
Conout("FileName: " + cValToChar(oError:FileName))
|
|
136
|
+
Conout("Operation: " + oError:Operation)
|
|
137
|
+
Conout("Args: " + cValToChar(oError:Args))
|
|
138
|
+
Conout("======================")
|
|
139
|
+
|
|
140
|
+
End Sequence
|
|
141
|
+
|
|
142
|
+
ErrorBlock(bOldError)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Database Lock Diagnosis
|
|
146
|
+
|
|
147
|
+
### RecLock with Timeout Check
|
|
148
|
+
|
|
149
|
+
Always use `.F.` (non-exclusive wait) and check the return value:
|
|
150
|
+
|
|
151
|
+
```advpl
|
|
152
|
+
DbSelectArea("SD1")
|
|
153
|
+
DbSetOrder(1)
|
|
154
|
+
|
|
155
|
+
If DbSeek(xFilial("SD1") + cDoc + cSerie)
|
|
156
|
+
// Try to lock with .F. (non-blocking)
|
|
157
|
+
If RecLock("SD1", .F.)
|
|
158
|
+
SD1->D1_TOTAL := nNewTotal
|
|
159
|
+
MsUnlock()
|
|
160
|
+
Conout(">>> Record updated successfully")
|
|
161
|
+
Else
|
|
162
|
+
Conout(">>> ERROR: Could not lock SD1 RecNo " + cValToChar(RecNo()))
|
|
163
|
+
Conout(">>> Another user/thread may be editing this record")
|
|
164
|
+
// Check who is locking:
|
|
165
|
+
// Use Protheus Monitor (SIGAMNT) or AppServer console
|
|
166
|
+
EndIf
|
|
167
|
+
EndIf
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Common Lock Issues
|
|
171
|
+
|
|
172
|
+
| Issue | Cause | Fix |
|
|
173
|
+
|-------|-------|-----|
|
|
174
|
+
| RecLock hangs indefinitely | Using `RecLock(cAlias, .T.)` - blocks forever | Use `RecLock(cAlias, .F.)` and check return |
|
|
175
|
+
| Lock not released | Missing `MsUnlock()` after RecLock | Always call `MsUnlock()` after writing |
|
|
176
|
+
| Deadlock between threads | Two threads locking records in different order | Lock records in a consistent order, keep locks short |
|
|
177
|
+
| Exclusive lock failure | Another process has shared lock | Schedule exclusive operations during off-hours |
|
|
178
|
+
|
|
179
|
+
## Performance Quick Checks
|
|
180
|
+
|
|
181
|
+
### Wrong Index (DbSetOrder)
|
|
182
|
+
|
|
183
|
+
Using the wrong index forces a full table scan:
|
|
184
|
+
|
|
185
|
+
```advpl
|
|
186
|
+
// WRONG - may use wrong index or full scan
|
|
187
|
+
DbSelectArea("SA1")
|
|
188
|
+
DbSetOrder(3) // Index 3 may not match your search key
|
|
189
|
+
DbSeek(xFilial("SA1") + cCodCli)
|
|
190
|
+
|
|
191
|
+
// RIGHT - verify the index matches your key
|
|
192
|
+
// Check SIX table for SA1 index composition
|
|
193
|
+
// Index 1 typically: A1_FILIAL + A1_COD + A1_LOJA
|
|
194
|
+
DbSelectArea("SA1")
|
|
195
|
+
DbSetOrder(1)
|
|
196
|
+
DbSeek(xFilial("SA1") + cCodCli + cLoja)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Array Growth in Loops
|
|
200
|
+
|
|
201
|
+
Pre-allocate arrays when the size is known:
|
|
202
|
+
|
|
203
|
+
```advpl
|
|
204
|
+
// SLOW - aAdd reallocates memory on every iteration
|
|
205
|
+
Local aResult := {}
|
|
206
|
+
While !Eof()
|
|
207
|
+
aAdd(aResult, {ALIAS->FIELD1, ALIAS->FIELD2})
|
|
208
|
+
DbSkip()
|
|
209
|
+
EndDo
|
|
210
|
+
|
|
211
|
+
// FAST - pre-allocate with aSize
|
|
212
|
+
Local nCount := RecCount() // or known count
|
|
213
|
+
Local aResult := Array(nCount)
|
|
214
|
+
Local nIdx := 0
|
|
215
|
+
While !Eof()
|
|
216
|
+
nIdx++
|
|
217
|
+
aResult[nIdx] := {ALIAS->FIELD1, ALIAS->FIELD2}
|
|
218
|
+
DbSkip()
|
|
219
|
+
EndDo
|
|
220
|
+
aSize(aResult, nIdx) // trim unused slots
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Embedded SQL vs ISAM
|
|
224
|
+
|
|
225
|
+
Use embedded SQL for complex queries; use ISAM for simple key lookups:
|
|
226
|
+
|
|
227
|
+
```advpl
|
|
228
|
+
// ISAM - good for single-record lookup by key
|
|
229
|
+
DbSelectArea("SA1")
|
|
230
|
+
DbSetOrder(1)
|
|
231
|
+
If DbSeek(xFilial("SA1") + cCodCli + cLoja)
|
|
232
|
+
cNome := SA1->A1_NOME
|
|
233
|
+
EndIf
|
|
234
|
+
|
|
235
|
+
// Embedded SQL - good for filtered/aggregated queries
|
|
236
|
+
Local cQuery := ""
|
|
237
|
+
cQuery += "SELECT D1_DOC, D1_SERIE, SUM(D1_TOTAL) AS TOTAL "
|
|
238
|
+
cQuery += "FROM " + RetSqlName("SD1") + " SD1 "
|
|
239
|
+
cQuery += "WHERE D1_FILIAL = '" + xFilial("SD1") + "' "
|
|
240
|
+
cQuery += "AND D1_EMISSAO >= '" + DtoS(dDataIni) + "' "
|
|
241
|
+
cQuery += "AND SD1.D_E_L_E_T_ = ' ' "
|
|
242
|
+
cQuery += "GROUP BY D1_DOC, D1_SERIE"
|
|
243
|
+
|
|
244
|
+
TCQuery cQuery New Alias "QRY_TMP"
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
See `performance-tips.md` for comprehensive optimization techniques.
|
|
248
|
+
|
|
249
|
+
## Common Mistakes When Debugging
|
|
250
|
+
|
|
251
|
+
| Mistake | Why It Is Wrong | Better Approach |
|
|
252
|
+
|---------|----------------|-----------------|
|
|
253
|
+
| Leaving Conout in production | Floods console, impacts performance | Use FWLogMsg with severity, remove debug Conouts before deploy |
|
|
254
|
+
| Not saving ErrorBlock | Overwrites global error handler | Always save with `bOld := ErrorBlock(...)` and restore after |
|
|
255
|
+
| Using MsgAlert for debug | Blocks execution, not visible on server | Use Conout or FWLogMsg for non-interactive debugging |
|
|
256
|
+
| Ignoring RecLock return value | Writes to unlocked record - data corruption | Always check `If RecLock(cAlias, .F.)` |
|
|
257
|
+
| Not checking ValType before operations | Misses NIL values that cause runtime errors | Add `ValType(xVar) == "C"` checks before string operations |
|
|
258
|
+
| Debugging on production environment | Risk of data corruption or downtime | Reproduce on development/QA environment first |
|
|
259
|
+
| Not reading the full stack trace | Fixes symptom, not root cause | Read from bottom to top: the root cause is usually deepest in the stack |
|
|
260
|
+
| Hardcoding file paths in debug | Works on your machine only | Use `GetSrvProfString()` or system paths |
|
|
261
|
+
|
|
262
|
+
## References
|
|
263
|
+
|
|
264
|
+
- `common-errors.md` - Top 50 ADVPL/TLPP errors with causes and solutions
|
|
265
|
+
- `performance-tips.md` - Performance optimization techniques with code examples
|